diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 746ecf5f98d20..f35b692fcd7fe 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -58,6 +58,8 @@ jobs: - 'integrations/terraform/Makefile' - 'integrations/terraform/examples/**' - 'integrations/terraform/templates/**' + # rendered doc changes + - 'docs/pages/reference/terraform-provider/**' lint-go: name: Lint (Go) diff --git a/.prettierignore b/.prettierignore index c62aa062ee93c..89ad032d04157 100644 --- a/.prettierignore +++ b/.prettierignore @@ -8,4 +8,4 @@ # Ignore WASM generated files: web/packages/teleport/src/ironrdp/pkg # Ignore generated mockServiceWorker file -web/.storybook/public/mockServiceWorker.js \ No newline at end of file +web/.storybook/public/mockServiceWorker.js diff --git a/api/client/client.go b/api/client/client.go index c5efa29d2493c..0313deb561e4c 100644 --- a/api/client/client.go +++ b/api/client/client.go @@ -3670,7 +3670,7 @@ func convertEnrichedResource(resource *proto.PaginatedResource) (*types.Enriched } else if r := resource.GetUserGroup(); r != nil { return &types.EnrichedResource{ResourceWithLabels: r, RequiresRequest: resource.RequiresRequest}, nil } else if r := resource.GetAppServer(); r != nil { - return &types.EnrichedResource{ResourceWithLabels: r, RequiresRequest: resource.RequiresRequest}, nil + return &types.EnrichedResource{ResourceWithLabels: r, Logins: resource.Logins, RequiresRequest: resource.RequiresRequest}, nil } else if r := resource.GetSAMLIdPServiceProvider(); r != nil { return &types.EnrichedResource{ResourceWithLabels: r, RequiresRequest: resource.RequiresRequest}, nil } else { diff --git a/api/client/client_test.go b/api/client/client_test.go index 33d0c1d8f9946..76181279b1e1a 100644 --- a/api/client/client_test.go +++ b/api/client/client_test.go @@ -46,7 +46,7 @@ func TestMain(m *testing.M) { } type pingService struct { - *proto.UnimplementedAuthServiceServer + proto.UnimplementedAuthServiceServer userAgentFromLastCallValue atomic.Value } @@ -192,7 +192,7 @@ func TestWaitForConnectionReady(t *testing.T) { } type listResourcesService struct { - *proto.UnimplementedAuthServiceServer + proto.UnimplementedAuthServiceServer } func (s *listResourcesService) ListResources(ctx context.Context, req *proto.ListResourcesRequest) (*proto.ListResourcesResponse, error) { @@ -732,6 +732,10 @@ func TestGetUnifiedResourcesWithLogins(t *testing.T) { Resource: &proto.PaginatedResource_WindowsDesktop{WindowsDesktop: &types.WindowsDesktopV3{}}, Logins: []string{"llama"}, }, + { + Resource: &proto.PaginatedResource_AppServer{AppServer: &types.AppServerV3{}}, + Logins: []string{"llama"}, + }, }, }, } @@ -753,6 +757,8 @@ func TestGetUnifiedResourcesWithLogins(t *testing.T) { assert.Equal(t, enriched.Logins, clt.resp.Resources[0].Logins) case *types.WindowsDesktopV3: assert.Equal(t, enriched.Logins, clt.resp.Resources[1].Logins) + case *types.AppServerV3: + assert.Equal(t, enriched.Logins, clt.resp.Resources[2].Logins) } } } diff --git a/api/client/credentials.go b/api/client/credentials.go index 02d9121da804d..1d3d54815db89 100644 --- a/api/client/credentials.go +++ b/api/client/credentials.go @@ -639,3 +639,62 @@ func (d *DynamicIdentityFileCreds) Expiry() (time.Time, bool) { return x509Cert.NotAfter, true } + +// KeyPair returns a Credential give a TLS key, certificate and CA certificates PEM-encoded. +// It behaves live LoadKeyPair except it doesn't read the TLS material from a file. +// This is useful when key and certs are not on the disk (e.g. environment variables). +// This should be preferred over manually building a tls.Config and calling LoadTLS +// as Credentials returned by KeyPair can report their expiry, which allows to warn +// the user in case of expired certificates. +func KeyPair(certPEM, keyPEM, caPEM []byte) (Credentials, error) { + if len(certPEM) == 0 { + return nil, trace.BadParameter("missing certificate PEM data") + } + if len(keyPEM) == 0 { + return nil, trace.BadParameter("missing private key PEM data") + } + return &staticKeypairCreds{ + certPEM: certPEM, + keyPEM: keyPEM, + caPEM: caPEM, + }, nil +} + +// staticKeypairCreds uses keypair certificates to provide client credentials. +type staticKeypairCreds struct { + certPEM []byte + keyPEM []byte + caPEM []byte +} + +// TLSConfig returns TLS configuration. +func (c *staticKeypairCreds) TLSConfig() (*tls.Config, error) { + cert, err := keys.X509KeyPair(c.certPEM, c.keyPEM) + if err != nil { + return nil, trace.Wrap(err) + } + + pool := x509.NewCertPool() + if ok := pool.AppendCertsFromPEM(c.caPEM); !ok { + return nil, trace.BadParameter("invalid TLS CA cert PEM") + } + + return configureTLS(&tls.Config{ + Certificates: []tls.Certificate{cert}, + RootCAs: pool, + }), nil +} + +// SSHClientConfig returns SSH configuration. +func (c *staticKeypairCreds) SSHClientConfig() (*ssh.ClientConfig, error) { + return nil, trace.NotImplemented("no ssh config") +} + +// Expiry returns the credential expiry. +func (c *staticKeypairCreds) Expiry() (time.Time, bool) { + cert, _, err := keys.X509Certificate(c.certPEM) + if err != nil { + return time.Time{}, false + } + return cert.NotAfter, true +} diff --git a/api/client/credentials_test.go b/api/client/credentials_test.go index b26857bf15a63..cb1a4ae90767e 100644 --- a/api/client/credentials_test.go +++ b/api/client/credentials_test.go @@ -203,6 +203,32 @@ func TestLoadKeyPair(t *testing.T) { require.False(t, ok, "expiry should be unknown on a broken credential") } +func TestKeyPair(t *testing.T) { + t.Parallel() + + // Load expected tls.Config. + expectedTLSConfig := getExpectedTLSConfig(t) + + // Load key pair from disk. + creds, err := KeyPair(tlsCert, keyPEM, tlsCACert) + require.NoError(t, err) + + // Build tls.Config and compare to expected tls.Config. + tlsConfig, err := creds.TLSConfig() + require.NoError(t, err) + requireEqualTLSConfig(t, expectedTLSConfig, tlsConfig) + + // Load invalid keypairs. + invalidIdentityCreds, err := KeyPair([]byte("invalid_cert"), []byte("invalid_key"), []byte("invalid_ca_cert")) + require.NoError(t, err) + _, err = invalidIdentityCreds.TLSConfig() + require.Error(t, err) + + // Load missing keypairs + _, err = KeyPair(nil, nil, nil) + require.Error(t, err) +} + func TestLoadProfile(t *testing.T) { t.Parallel() profileName := "proxy.example.com" diff --git a/api/client/joinservice_test.go b/api/client/joinservice_test.go index a8a8509866d67..c0068f4be47f7 100644 --- a/api/client/joinservice_test.go +++ b/api/client/joinservice_test.go @@ -34,7 +34,7 @@ import ( ) type mockJoinServiceServer struct { - *proto.UnimplementedJoinServiceServer + proto.UnimplementedJoinServiceServer registerUsingTPMMethod func(srv proto.JoinService_RegisterUsingTPMMethodServer) error } diff --git a/api/client/proxy/client_test.go b/api/client/proxy/client_test.go index 7cb788e5e76af..6a1671aaf8f37 100644 --- a/api/client/proxy/client_test.go +++ b/api/client/proxy/client_test.go @@ -114,16 +114,15 @@ type fakeGRPCServer struct { } type fakeAuthServer struct { - *proto.UnimplementedAuthServiceServer + proto.UnimplementedAuthServiceServer listener net.Listener srv *grpc.Server } func newFakeAuthServer(t *testing.T, conn net.Conn) *fakeAuthServer { f := &fakeAuthServer{ - listener: newOneShotListener(conn), - UnimplementedAuthServiceServer: &proto.UnimplementedAuthServiceServer{}, - srv: grpc.NewServer(), + listener: newOneShotListener(conn), + srv: grpc.NewServer(), } t.Cleanup(f.Stop) diff --git a/api/constants/constants.go b/api/constants/constants.go index c91517a6839c6..ee5e604975da1 100644 --- a/api/constants/constants.go +++ b/api/constants/constants.go @@ -500,4 +500,8 @@ const ( EnvVarTerraformRetryMaxTries = "TF_TELEPORT_RETRY_MAX_TRIES" // EnvVarTerraformDialTimeoutDuration is the environment variable configuring the Terraform provider dial timeout. EnvVarTerraformDialTimeoutDuration = "TF_TELEPORT_DIAL_TIMEOUT_DURATION" + // EnvVarTerraformJoinMethod is the environment variable configuring the Terraform provider native MachineID join method. + EnvVarTerraformJoinMethod = "TF_TELEPORT_JOIN_METHOD" + // EnvVarTerraformJoinToken is the environment variable configuring the Terraform provider native MachineID join token. + EnvVarTerraformJoinToken = "TF_TELEPORT_JOIN_TOKEN" ) diff --git a/api/gen/proto/go/teleport/accessgraph/v1/secrets_service_grpc.pb.go b/api/gen/proto/go/teleport/accessgraph/v1/secrets_service_grpc.pb.go index bc1e09264f36a..3c131c843dd49 100644 --- a/api/gen/proto/go/teleport/accessgraph/v1/secrets_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/accessgraph/v1/secrets_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/access_graph/v1/secrets_service.proto @@ -29,8 +29,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( SecretsScannerService_ReportAuthorizedKeys_FullMethodName = "/teleport.access_graph.v1.SecretsScannerService/ReportAuthorizedKeys" @@ -46,7 +46,7 @@ type SecretsScannerServiceClient interface { // ReportAuthorizedKeys is used by Teleport SSH nodes to report authorized keys // that could be used to bypass Teleport. // The client (Teleport SSH Node) should authenticate using the certificate-key pair signed by Teleport HostCA. - ReportAuthorizedKeys(ctx context.Context, opts ...grpc.CallOption) (SecretsScannerService_ReportAuthorizedKeysClient, error) + ReportAuthorizedKeys(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ReportAuthorizedKeysRequest, ReportAuthorizedKeysResponse], error) // ReportSecrets is used by trusted devices to report secrets found on the host that could be used to bypass Teleport. // The client (device) should first authenticate using the [ReportSecretsRequest.device_assertion] flow. Please refer to // the [teleport.devicetrust.v1.AssertDeviceRequest] and [teleport.devicetrust.v1.AssertDeviceResponse] messages for more details. @@ -60,7 +60,7 @@ type SecretsScannerServiceClient interface { // // Any failure in the assertion ceremony will result in the stream being terminated by the server. All secrets // reported by the client before the assertion terminates will be ignored and result in the stream being terminated. - ReportSecrets(ctx context.Context, opts ...grpc.CallOption) (SecretsScannerService_ReportSecretsClient, error) + ReportSecrets(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ReportSecretsRequest, ReportSecretsResponse], error) } type secretsScannerServiceClient struct { @@ -71,80 +71,42 @@ func NewSecretsScannerServiceClient(cc grpc.ClientConnInterface) SecretsScannerS return &secretsScannerServiceClient{cc} } -func (c *secretsScannerServiceClient) ReportAuthorizedKeys(ctx context.Context, opts ...grpc.CallOption) (SecretsScannerService_ReportAuthorizedKeysClient, error) { +func (c *secretsScannerServiceClient) ReportAuthorizedKeys(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ReportAuthorizedKeysRequest, ReportAuthorizedKeysResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &SecretsScannerService_ServiceDesc.Streams[0], SecretsScannerService_ReportAuthorizedKeys_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &secretsScannerServiceReportAuthorizedKeysClient{ClientStream: stream} + x := &grpc.GenericClientStream[ReportAuthorizedKeysRequest, ReportAuthorizedKeysResponse]{ClientStream: stream} return x, nil } -type SecretsScannerService_ReportAuthorizedKeysClient interface { - Send(*ReportAuthorizedKeysRequest) error - Recv() (*ReportAuthorizedKeysResponse, error) - grpc.ClientStream -} - -type secretsScannerServiceReportAuthorizedKeysClient struct { - grpc.ClientStream -} - -func (x *secretsScannerServiceReportAuthorizedKeysClient) Send(m *ReportAuthorizedKeysRequest) error { - return x.ClientStream.SendMsg(m) -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type SecretsScannerService_ReportAuthorizedKeysClient = grpc.BidiStreamingClient[ReportAuthorizedKeysRequest, ReportAuthorizedKeysResponse] -func (x *secretsScannerServiceReportAuthorizedKeysClient) Recv() (*ReportAuthorizedKeysResponse, error) { - m := new(ReportAuthorizedKeysResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func (c *secretsScannerServiceClient) ReportSecrets(ctx context.Context, opts ...grpc.CallOption) (SecretsScannerService_ReportSecretsClient, error) { +func (c *secretsScannerServiceClient) ReportSecrets(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ReportSecretsRequest, ReportSecretsResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &SecretsScannerService_ServiceDesc.Streams[1], SecretsScannerService_ReportSecrets_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &secretsScannerServiceReportSecretsClient{ClientStream: stream} + x := &grpc.GenericClientStream[ReportSecretsRequest, ReportSecretsResponse]{ClientStream: stream} return x, nil } -type SecretsScannerService_ReportSecretsClient interface { - Send(*ReportSecretsRequest) error - Recv() (*ReportSecretsResponse, error) - grpc.ClientStream -} - -type secretsScannerServiceReportSecretsClient struct { - grpc.ClientStream -} - -func (x *secretsScannerServiceReportSecretsClient) Send(m *ReportSecretsRequest) error { - return x.ClientStream.SendMsg(m) -} - -func (x *secretsScannerServiceReportSecretsClient) Recv() (*ReportSecretsResponse, error) { - m := new(ReportSecretsResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type SecretsScannerService_ReportSecretsClient = grpc.BidiStreamingClient[ReportSecretsRequest, ReportSecretsResponse] // SecretsScannerServiceServer is the server API for SecretsScannerService service. // All implementations must embed UnimplementedSecretsScannerServiceServer -// for forward compatibility +// for forward compatibility. // // SecretsScannerService provides methods for Access Graph Secret Scanner functionality. type SecretsScannerServiceServer interface { // ReportAuthorizedKeys is used by Teleport SSH nodes to report authorized keys // that could be used to bypass Teleport. // The client (Teleport SSH Node) should authenticate using the certificate-key pair signed by Teleport HostCA. - ReportAuthorizedKeys(SecretsScannerService_ReportAuthorizedKeysServer) error + ReportAuthorizedKeys(grpc.BidiStreamingServer[ReportAuthorizedKeysRequest, ReportAuthorizedKeysResponse]) error // ReportSecrets is used by trusted devices to report secrets found on the host that could be used to bypass Teleport. // The client (device) should first authenticate using the [ReportSecretsRequest.device_assertion] flow. Please refer to // the [teleport.devicetrust.v1.AssertDeviceRequest] and [teleport.devicetrust.v1.AssertDeviceResponse] messages for more details. @@ -158,21 +120,25 @@ type SecretsScannerServiceServer interface { // // Any failure in the assertion ceremony will result in the stream being terminated by the server. All secrets // reported by the client before the assertion terminates will be ignored and result in the stream being terminated. - ReportSecrets(SecretsScannerService_ReportSecretsServer) error + ReportSecrets(grpc.BidiStreamingServer[ReportSecretsRequest, ReportSecretsResponse]) error mustEmbedUnimplementedSecretsScannerServiceServer() } -// UnimplementedSecretsScannerServiceServer must be embedded to have forward compatible implementations. -type UnimplementedSecretsScannerServiceServer struct { -} +// UnimplementedSecretsScannerServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedSecretsScannerServiceServer struct{} -func (UnimplementedSecretsScannerServiceServer) ReportAuthorizedKeys(SecretsScannerService_ReportAuthorizedKeysServer) error { +func (UnimplementedSecretsScannerServiceServer) ReportAuthorizedKeys(grpc.BidiStreamingServer[ReportAuthorizedKeysRequest, ReportAuthorizedKeysResponse]) error { return status.Errorf(codes.Unimplemented, "method ReportAuthorizedKeys not implemented") } -func (UnimplementedSecretsScannerServiceServer) ReportSecrets(SecretsScannerService_ReportSecretsServer) error { +func (UnimplementedSecretsScannerServiceServer) ReportSecrets(grpc.BidiStreamingServer[ReportSecretsRequest, ReportSecretsResponse]) error { return status.Errorf(codes.Unimplemented, "method ReportSecrets not implemented") } func (UnimplementedSecretsScannerServiceServer) mustEmbedUnimplementedSecretsScannerServiceServer() {} +func (UnimplementedSecretsScannerServiceServer) testEmbeddedByValue() {} // UnsafeSecretsScannerServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to SecretsScannerServiceServer will @@ -182,60 +148,29 @@ type UnsafeSecretsScannerServiceServer interface { } func RegisterSecretsScannerServiceServer(s grpc.ServiceRegistrar, srv SecretsScannerServiceServer) { + // If the following call pancis, it indicates UnimplementedSecretsScannerServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&SecretsScannerService_ServiceDesc, srv) } func _SecretsScannerService_ReportAuthorizedKeys_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(SecretsScannerServiceServer).ReportAuthorizedKeys(&secretsScannerServiceReportAuthorizedKeysServer{ServerStream: stream}) -} - -type SecretsScannerService_ReportAuthorizedKeysServer interface { - Send(*ReportAuthorizedKeysResponse) error - Recv() (*ReportAuthorizedKeysRequest, error) - grpc.ServerStream -} - -type secretsScannerServiceReportAuthorizedKeysServer struct { - grpc.ServerStream + return srv.(SecretsScannerServiceServer).ReportAuthorizedKeys(&grpc.GenericServerStream[ReportAuthorizedKeysRequest, ReportAuthorizedKeysResponse]{ServerStream: stream}) } -func (x *secretsScannerServiceReportAuthorizedKeysServer) Send(m *ReportAuthorizedKeysResponse) error { - return x.ServerStream.SendMsg(m) -} - -func (x *secretsScannerServiceReportAuthorizedKeysServer) Recv() (*ReportAuthorizedKeysRequest, error) { - m := new(ReportAuthorizedKeysRequest) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type SecretsScannerService_ReportAuthorizedKeysServer = grpc.BidiStreamingServer[ReportAuthorizedKeysRequest, ReportAuthorizedKeysResponse] func _SecretsScannerService_ReportSecrets_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(SecretsScannerServiceServer).ReportSecrets(&secretsScannerServiceReportSecretsServer{ServerStream: stream}) + return srv.(SecretsScannerServiceServer).ReportSecrets(&grpc.GenericServerStream[ReportSecretsRequest, ReportSecretsResponse]{ServerStream: stream}) } -type SecretsScannerService_ReportSecretsServer interface { - Send(*ReportSecretsResponse) error - Recv() (*ReportSecretsRequest, error) - grpc.ServerStream -} - -type secretsScannerServiceReportSecretsServer struct { - grpc.ServerStream -} - -func (x *secretsScannerServiceReportSecretsServer) Send(m *ReportSecretsResponse) error { - return x.ServerStream.SendMsg(m) -} - -func (x *secretsScannerServiceReportSecretsServer) Recv() (*ReportSecretsRequest, error) { - m := new(ReportSecretsRequest) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type SecretsScannerService_ReportSecretsServer = grpc.BidiStreamingServer[ReportSecretsRequest, ReportSecretsResponse] // SecretsScannerService_ServiceDesc is the grpc.ServiceDesc for SecretsScannerService service. // It's only intended for direct use with grpc.RegisterService, diff --git a/api/gen/proto/go/teleport/accesslist/v1/accesslist_service_grpc.pb.go b/api/gen/proto/go/teleport/accesslist/v1/accesslist_service_grpc.pb.go index 13c53d238ee29..ae47a9a39038c 100644 --- a/api/gen/proto/go/teleport/accesslist/v1/accesslist_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/accesslist/v1/accesslist_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/accesslist/v1/accesslist_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( AccessListService_GetAccessLists_FullMethodName = "/teleport.accesslist.v1.AccessListService/GetAccessLists" @@ -377,7 +377,7 @@ func (c *accessListServiceClient) GetSuggestedAccessLists(ctx context.Context, i // AccessListServiceServer is the server API for AccessListService service. // All implementations must embed UnimplementedAccessListServiceServer -// for forward compatibility +// for forward compatibility. // // AccessListService provides CRUD methods for Access List resources. type AccessListServiceServer interface { @@ -443,9 +443,12 @@ type AccessListServiceServer interface { mustEmbedUnimplementedAccessListServiceServer() } -// UnimplementedAccessListServiceServer must be embedded to have forward compatible implementations. -type UnimplementedAccessListServiceServer struct { -} +// UnimplementedAccessListServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedAccessListServiceServer struct{} func (UnimplementedAccessListServiceServer) GetAccessLists(context.Context, *GetAccessListsRequest) (*GetAccessListsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetAccessLists not implemented") @@ -520,6 +523,7 @@ func (UnimplementedAccessListServiceServer) GetSuggestedAccessLists(context.Cont return nil, status.Errorf(codes.Unimplemented, "method GetSuggestedAccessLists not implemented") } func (UnimplementedAccessListServiceServer) mustEmbedUnimplementedAccessListServiceServer() {} +func (UnimplementedAccessListServiceServer) testEmbeddedByValue() {} // UnsafeAccessListServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to AccessListServiceServer will @@ -529,6 +533,13 @@ type UnsafeAccessListServiceServer interface { } func RegisterAccessListServiceServer(s grpc.ServiceRegistrar, srv AccessListServiceServer) { + // If the following call pancis, it indicates UnimplementedAccessListServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&AccessListService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/accessmonitoringrules/v1/access_monitoring_rules_service_grpc.pb.go b/api/gen/proto/go/teleport/accessmonitoringrules/v1/access_monitoring_rules_service_grpc.pb.go index 71c4bbcd01f27..d1e98293dacba 100644 --- a/api/gen/proto/go/teleport/accessmonitoringrules/v1/access_monitoring_rules_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/accessmonitoringrules/v1/access_monitoring_rules_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/accessmonitoringrules/v1/access_monitoring_rules_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( AccessMonitoringRulesService_CreateAccessMonitoringRule_FullMethodName = "/teleport.accessmonitoringrules.v1.AccessMonitoringRulesService/CreateAccessMonitoringRule" @@ -145,7 +145,7 @@ func (c *accessMonitoringRulesServiceClient) ListAccessMonitoringRulesWithFilter // AccessMonitoringRulesServiceServer is the server API for AccessMonitoringRulesService service. // All implementations must embed UnimplementedAccessMonitoringRulesServiceServer -// for forward compatibility +// for forward compatibility. // // AccessMonitoringRulesService provides CRUD methods for Access Monitoring Rules resources. type AccessMonitoringRulesServiceServer interface { @@ -166,9 +166,12 @@ type AccessMonitoringRulesServiceServer interface { mustEmbedUnimplementedAccessMonitoringRulesServiceServer() } -// UnimplementedAccessMonitoringRulesServiceServer must be embedded to have forward compatible implementations. -type UnimplementedAccessMonitoringRulesServiceServer struct { -} +// UnimplementedAccessMonitoringRulesServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedAccessMonitoringRulesServiceServer struct{} func (UnimplementedAccessMonitoringRulesServiceServer) CreateAccessMonitoringRule(context.Context, *CreateAccessMonitoringRuleRequest) (*AccessMonitoringRule, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateAccessMonitoringRule not implemented") @@ -193,6 +196,7 @@ func (UnimplementedAccessMonitoringRulesServiceServer) ListAccessMonitoringRules } func (UnimplementedAccessMonitoringRulesServiceServer) mustEmbedUnimplementedAccessMonitoringRulesServiceServer() { } +func (UnimplementedAccessMonitoringRulesServiceServer) testEmbeddedByValue() {} // UnsafeAccessMonitoringRulesServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to AccessMonitoringRulesServiceServer will @@ -202,6 +206,13 @@ type UnsafeAccessMonitoringRulesServiceServer interface { } func RegisterAccessMonitoringRulesServiceServer(s grpc.ServiceRegistrar, srv AccessMonitoringRulesServiceServer) { + // If the following call pancis, it indicates UnimplementedAccessMonitoringRulesServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&AccessMonitoringRulesService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/auditlog/v1/auditlog_grpc.pb.go b/api/gen/proto/go/teleport/auditlog/v1/auditlog_grpc.pb.go index e72abf53a92c6..f8a7779af753a 100644 --- a/api/gen/proto/go/teleport/auditlog/v1/auditlog_grpc.pb.go +++ b/api/gen/proto/go/teleport/auditlog/v1/auditlog_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/auditlog/v1/auditlog.proto @@ -29,8 +29,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( AuditLogService_StreamUnstructuredSessionEvents_FullMethodName = "/teleport.auditlog.v1.AuditLogService/StreamUnstructuredSessionEvents" @@ -45,7 +45,7 @@ const ( type AuditLogServiceClient interface { // StreamUnstructuredSessionEvents streams audit events from a given session recording in an unstructured format. // This endpoint is used by the event handler to retrieve the session events as JSON. - StreamUnstructuredSessionEvents(ctx context.Context, in *StreamUnstructuredSessionEventsRequest, opts ...grpc.CallOption) (AuditLogService_StreamUnstructuredSessionEventsClient, error) + StreamUnstructuredSessionEvents(ctx context.Context, in *StreamUnstructuredSessionEventsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[EventUnstructured], error) // GetUnstructuredEvents gets events from the audit log in an unstructured format. // This endpoint is used by the event handler to retrieve the events as JSON. GetUnstructuredEvents(ctx context.Context, in *GetUnstructuredEventsRequest, opts ...grpc.CallOption) (*EventsUnstructured, error) @@ -59,13 +59,13 @@ func NewAuditLogServiceClient(cc grpc.ClientConnInterface) AuditLogServiceClient return &auditLogServiceClient{cc} } -func (c *auditLogServiceClient) StreamUnstructuredSessionEvents(ctx context.Context, in *StreamUnstructuredSessionEventsRequest, opts ...grpc.CallOption) (AuditLogService_StreamUnstructuredSessionEventsClient, error) { +func (c *auditLogServiceClient) StreamUnstructuredSessionEvents(ctx context.Context, in *StreamUnstructuredSessionEventsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[EventUnstructured], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &AuditLogService_ServiceDesc.Streams[0], AuditLogService_StreamUnstructuredSessionEvents_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &auditLogServiceStreamUnstructuredSessionEventsClient{ClientStream: stream} + x := &grpc.GenericClientStream[StreamUnstructuredSessionEventsRequest, EventUnstructured]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } @@ -75,22 +75,8 @@ func (c *auditLogServiceClient) StreamUnstructuredSessionEvents(ctx context.Cont return x, nil } -type AuditLogService_StreamUnstructuredSessionEventsClient interface { - Recv() (*EventUnstructured, error) - grpc.ClientStream -} - -type auditLogServiceStreamUnstructuredSessionEventsClient struct { - grpc.ClientStream -} - -func (x *auditLogServiceStreamUnstructuredSessionEventsClient) Recv() (*EventUnstructured, error) { - m := new(EventUnstructured) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type AuditLogService_StreamUnstructuredSessionEventsClient = grpc.ServerStreamingClient[EventUnstructured] func (c *auditLogServiceClient) GetUnstructuredEvents(ctx context.Context, in *GetUnstructuredEventsRequest, opts ...grpc.CallOption) (*EventsUnstructured, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) @@ -104,30 +90,34 @@ func (c *auditLogServiceClient) GetUnstructuredEvents(ctx context.Context, in *G // AuditLogServiceServer is the server API for AuditLogService service. // All implementations must embed UnimplementedAuditLogServiceServer -// for forward compatibility +// for forward compatibility. // // AuditLogService provides methods to access audit log. type AuditLogServiceServer interface { // StreamUnstructuredSessionEvents streams audit events from a given session recording in an unstructured format. // This endpoint is used by the event handler to retrieve the session events as JSON. - StreamUnstructuredSessionEvents(*StreamUnstructuredSessionEventsRequest, AuditLogService_StreamUnstructuredSessionEventsServer) error + StreamUnstructuredSessionEvents(*StreamUnstructuredSessionEventsRequest, grpc.ServerStreamingServer[EventUnstructured]) error // GetUnstructuredEvents gets events from the audit log in an unstructured format. // This endpoint is used by the event handler to retrieve the events as JSON. GetUnstructuredEvents(context.Context, *GetUnstructuredEventsRequest) (*EventsUnstructured, error) mustEmbedUnimplementedAuditLogServiceServer() } -// UnimplementedAuditLogServiceServer must be embedded to have forward compatible implementations. -type UnimplementedAuditLogServiceServer struct { -} +// UnimplementedAuditLogServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedAuditLogServiceServer struct{} -func (UnimplementedAuditLogServiceServer) StreamUnstructuredSessionEvents(*StreamUnstructuredSessionEventsRequest, AuditLogService_StreamUnstructuredSessionEventsServer) error { +func (UnimplementedAuditLogServiceServer) StreamUnstructuredSessionEvents(*StreamUnstructuredSessionEventsRequest, grpc.ServerStreamingServer[EventUnstructured]) error { return status.Errorf(codes.Unimplemented, "method StreamUnstructuredSessionEvents not implemented") } func (UnimplementedAuditLogServiceServer) GetUnstructuredEvents(context.Context, *GetUnstructuredEventsRequest) (*EventsUnstructured, error) { return nil, status.Errorf(codes.Unimplemented, "method GetUnstructuredEvents not implemented") } func (UnimplementedAuditLogServiceServer) mustEmbedUnimplementedAuditLogServiceServer() {} +func (UnimplementedAuditLogServiceServer) testEmbeddedByValue() {} // UnsafeAuditLogServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to AuditLogServiceServer will @@ -137,6 +127,13 @@ type UnsafeAuditLogServiceServer interface { } func RegisterAuditLogServiceServer(s grpc.ServiceRegistrar, srv AuditLogServiceServer) { + // If the following call pancis, it indicates UnimplementedAuditLogServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&AuditLogService_ServiceDesc, srv) } @@ -145,21 +142,11 @@ func _AuditLogService_StreamUnstructuredSessionEvents_Handler(srv interface{}, s if err := stream.RecvMsg(m); err != nil { return err } - return srv.(AuditLogServiceServer).StreamUnstructuredSessionEvents(m, &auditLogServiceStreamUnstructuredSessionEventsServer{ServerStream: stream}) -} - -type AuditLogService_StreamUnstructuredSessionEventsServer interface { - Send(*EventUnstructured) error - grpc.ServerStream + return srv.(AuditLogServiceServer).StreamUnstructuredSessionEvents(m, &grpc.GenericServerStream[StreamUnstructuredSessionEventsRequest, EventUnstructured]{ServerStream: stream}) } -type auditLogServiceStreamUnstructuredSessionEventsServer struct { - grpc.ServerStream -} - -func (x *auditLogServiceStreamUnstructuredSessionEventsServer) Send(m *EventUnstructured) error { - return x.ServerStream.SendMsg(m) -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type AuditLogService_StreamUnstructuredSessionEventsServer = grpc.ServerStreamingServer[EventUnstructured] func _AuditLogService_GetUnstructuredEvents_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetUnstructuredEventsRequest) diff --git a/api/gen/proto/go/teleport/clusterconfig/v1/clusterconfig_service_grpc.pb.go b/api/gen/proto/go/teleport/clusterconfig/v1/clusterconfig_service_grpc.pb.go index 905e77dac9543..e0863476c8118 100644 --- a/api/gen/proto/go/teleport/clusterconfig/v1/clusterconfig_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/clusterconfig/v1/clusterconfig_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/clusterconfig/v1/clusterconfig_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( ClusterConfigService_GetClusterNetworkingConfig_FullMethodName = "/teleport.clusterconfig.v1.ClusterConfigService/GetClusterNetworkingConfig" @@ -301,7 +301,7 @@ func (c *clusterConfigServiceClient) ResetAccessGraphSettings(ctx context.Contex // ClusterConfigServiceServer is the server API for ClusterConfigService service. // All implementations must embed UnimplementedClusterConfigServiceServer -// for forward compatibility +// for forward compatibility. // // ClusterConfigService provides methods to manage cluster configuration resources. type ClusterConfigServiceServer interface { @@ -346,9 +346,12 @@ type ClusterConfigServiceServer interface { mustEmbedUnimplementedClusterConfigServiceServer() } -// UnimplementedClusterConfigServiceServer must be embedded to have forward compatible implementations. -type UnimplementedClusterConfigServiceServer struct { -} +// UnimplementedClusterConfigServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedClusterConfigServiceServer struct{} func (UnimplementedClusterConfigServiceServer) GetClusterNetworkingConfig(context.Context, *GetClusterNetworkingConfigRequest) (*types.ClusterNetworkingConfigV2, error) { return nil, status.Errorf(codes.Unimplemented, "method GetClusterNetworkingConfig not implemented") @@ -408,6 +411,7 @@ func (UnimplementedClusterConfigServiceServer) ResetAccessGraphSettings(context. return nil, status.Errorf(codes.Unimplemented, "method ResetAccessGraphSettings not implemented") } func (UnimplementedClusterConfigServiceServer) mustEmbedUnimplementedClusterConfigServiceServer() {} +func (UnimplementedClusterConfigServiceServer) testEmbeddedByValue() {} // UnsafeClusterConfigServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to ClusterConfigServiceServer will @@ -417,6 +421,13 @@ type UnsafeClusterConfigServiceServer interface { } func RegisterClusterConfigServiceServer(s grpc.ServiceRegistrar, srv ClusterConfigServiceServer) { + // If the following call pancis, it indicates UnimplementedClusterConfigServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&ClusterConfigService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/crownjewel/v1/crownjewel_service_grpc.pb.go b/api/gen/proto/go/teleport/crownjewel/v1/crownjewel_service_grpc.pb.go index 5098686592a34..653a5bf5e4295 100644 --- a/api/gen/proto/go/teleport/crownjewel/v1/crownjewel_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/crownjewel/v1/crownjewel_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/crownjewel/v1/crownjewel_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( CrownJewelService_CreateCrownJewel_FullMethodName = "/teleport.crownjewel.v1.CrownJewelService/CreateCrownJewel" @@ -132,7 +132,7 @@ func (c *crownJewelServiceClient) DeleteCrownJewel(ctx context.Context, in *Dele // CrownJewelServiceServer is the server API for CrownJewelService service. // All implementations must embed UnimplementedCrownJewelServiceServer -// for forward compatibility +// for forward compatibility. // // CrownJewelService is a service that provides methods to manage CrownJewels. type CrownJewelServiceServer interface { @@ -151,9 +151,12 @@ type CrownJewelServiceServer interface { mustEmbedUnimplementedCrownJewelServiceServer() } -// UnimplementedCrownJewelServiceServer must be embedded to have forward compatible implementations. -type UnimplementedCrownJewelServiceServer struct { -} +// UnimplementedCrownJewelServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedCrownJewelServiceServer struct{} func (UnimplementedCrownJewelServiceServer) CreateCrownJewel(context.Context, *CreateCrownJewelRequest) (*CrownJewel, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateCrownJewel not implemented") @@ -174,6 +177,7 @@ func (UnimplementedCrownJewelServiceServer) DeleteCrownJewel(context.Context, *D return nil, status.Errorf(codes.Unimplemented, "method DeleteCrownJewel not implemented") } func (UnimplementedCrownJewelServiceServer) mustEmbedUnimplementedCrownJewelServiceServer() {} +func (UnimplementedCrownJewelServiceServer) testEmbeddedByValue() {} // UnsafeCrownJewelServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to CrownJewelServiceServer will @@ -183,6 +187,13 @@ type UnsafeCrownJewelServiceServer interface { } func RegisterCrownJewelServiceServer(s grpc.ServiceRegistrar, srv CrownJewelServiceServer) { + // If the following call pancis, it indicates UnimplementedCrownJewelServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&CrownJewelService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/dbobject/v1/dbobject_service_grpc.pb.go b/api/gen/proto/go/teleport/dbobject/v1/dbobject_service_grpc.pb.go index 9b87181d0a7e9..05c0e34682bef 100644 --- a/api/gen/proto/go/teleport/dbobject/v1/dbobject_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/dbobject/v1/dbobject_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/dbobject/v1/dbobject_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( DatabaseObjectService_GetDatabaseObject_FullMethodName = "/teleport.dbobject.v1.DatabaseObjectService/GetDatabaseObject" @@ -143,7 +143,7 @@ func (c *databaseObjectServiceClient) DeleteDatabaseObject(ctx context.Context, // DatabaseObjectServiceServer is the server API for DatabaseObjectService service. // All implementations must embed UnimplementedDatabaseObjectServiceServer -// for forward compatibility +// for forward compatibility. // // DatabaseObjectService provides methods to manage Teleport DatabaseObjects type DatabaseObjectServiceServer interface { @@ -173,9 +173,12 @@ type DatabaseObjectServiceServer interface { mustEmbedUnimplementedDatabaseObjectServiceServer() } -// UnimplementedDatabaseObjectServiceServer must be embedded to have forward compatible implementations. -type UnimplementedDatabaseObjectServiceServer struct { -} +// UnimplementedDatabaseObjectServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedDatabaseObjectServiceServer struct{} func (UnimplementedDatabaseObjectServiceServer) GetDatabaseObject(context.Context, *GetDatabaseObjectRequest) (*DatabaseObject, error) { return nil, status.Errorf(codes.Unimplemented, "method GetDatabaseObject not implemented") @@ -196,6 +199,7 @@ func (UnimplementedDatabaseObjectServiceServer) DeleteDatabaseObject(context.Con return nil, status.Errorf(codes.Unimplemented, "method DeleteDatabaseObject not implemented") } func (UnimplementedDatabaseObjectServiceServer) mustEmbedUnimplementedDatabaseObjectServiceServer() {} +func (UnimplementedDatabaseObjectServiceServer) testEmbeddedByValue() {} // UnsafeDatabaseObjectServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to DatabaseObjectServiceServer will @@ -205,6 +209,13 @@ type UnsafeDatabaseObjectServiceServer interface { } func RegisterDatabaseObjectServiceServer(s grpc.ServiceRegistrar, srv DatabaseObjectServiceServer) { + // If the following call pancis, it indicates UnimplementedDatabaseObjectServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&DatabaseObjectService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/dbobjectimportrule/v1/dbobjectimportrule_service_grpc.pb.go b/api/gen/proto/go/teleport/dbobjectimportrule/v1/dbobjectimportrule_service_grpc.pb.go index f92f31efe1ff6..33a30ef154567 100644 --- a/api/gen/proto/go/teleport/dbobjectimportrule/v1/dbobjectimportrule_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/dbobjectimportrule/v1/dbobjectimportrule_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/dbobjectimportrule/v1/dbobjectimportrule_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( DatabaseObjectImportRuleService_GetDatabaseObjectImportRule_FullMethodName = "/teleport.dbobjectimportrule.v1.DatabaseObjectImportRuleService/GetDatabaseObjectImportRule" @@ -143,7 +143,7 @@ func (c *databaseObjectImportRuleServiceClient) DeleteDatabaseObjectImportRule(c // DatabaseObjectImportRuleServiceServer is the server API for DatabaseObjectImportRuleService service. // All implementations must embed UnimplementedDatabaseObjectImportRuleServiceServer -// for forward compatibility +// for forward compatibility. // // DatabaseObjectImportRuleService provides methods to manage Teleport DatabaseObjectImportRules type DatabaseObjectImportRuleServiceServer interface { @@ -173,9 +173,12 @@ type DatabaseObjectImportRuleServiceServer interface { mustEmbedUnimplementedDatabaseObjectImportRuleServiceServer() } -// UnimplementedDatabaseObjectImportRuleServiceServer must be embedded to have forward compatible implementations. -type UnimplementedDatabaseObjectImportRuleServiceServer struct { -} +// UnimplementedDatabaseObjectImportRuleServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedDatabaseObjectImportRuleServiceServer struct{} func (UnimplementedDatabaseObjectImportRuleServiceServer) GetDatabaseObjectImportRule(context.Context, *GetDatabaseObjectImportRuleRequest) (*DatabaseObjectImportRule, error) { return nil, status.Errorf(codes.Unimplemented, "method GetDatabaseObjectImportRule not implemented") @@ -197,6 +200,7 @@ func (UnimplementedDatabaseObjectImportRuleServiceServer) DeleteDatabaseObjectIm } func (UnimplementedDatabaseObjectImportRuleServiceServer) mustEmbedUnimplementedDatabaseObjectImportRuleServiceServer() { } +func (UnimplementedDatabaseObjectImportRuleServiceServer) testEmbeddedByValue() {} // UnsafeDatabaseObjectImportRuleServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to DatabaseObjectImportRuleServiceServer will @@ -206,6 +210,13 @@ type UnsafeDatabaseObjectImportRuleServiceServer interface { } func RegisterDatabaseObjectImportRuleServiceServer(s grpc.ServiceRegistrar, srv DatabaseObjectImportRuleServiceServer) { + // If the following call pancis, it indicates UnimplementedDatabaseObjectImportRuleServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&DatabaseObjectImportRuleService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/devicetrust/v1/devicetrust_service_grpc.pb.go b/api/gen/proto/go/teleport/devicetrust/v1/devicetrust_service_grpc.pb.go index 86be79eb248cf..efe987efc84da 100644 --- a/api/gen/proto/go/teleport/devicetrust/v1/devicetrust_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/devicetrust/v1/devicetrust_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/devicetrust/v1/devicetrust_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( DeviceTrustService_CreateDevice_FullMethodName = "/teleport.devicetrust.v1.DeviceTrustService/CreateDevice" @@ -141,7 +141,7 @@ type DeviceTrustServiceClient interface { // <- TPMEnrollChallenge (server) // -> TPMEnrollChallengeResponse // <- EnrollDeviceSuccess - EnrollDevice(ctx context.Context, opts ...grpc.CallOption) (DeviceTrustService_EnrollDeviceClient, error) + EnrollDevice(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[EnrollDeviceRequest, EnrollDeviceResponse], error) // AuthenticateDevice performs the device authentication ceremony. // // Device authentication exchanges existing user certificates without device @@ -149,7 +149,7 @@ type DeviceTrustServiceClient interface { // certificates allow the user to perform device-aware actions. // // Only registered and enrolled devices may perform device authentication. - AuthenticateDevice(ctx context.Context, opts ...grpc.CallOption) (DeviceTrustService_AuthenticateDeviceClient, error) + AuthenticateDevice(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[AuthenticateDeviceRequest, AuthenticateDeviceResponse], error) // ConfirmDeviceWebAuthentication finalizes the device web authentication // ceremony started by the creation of a DeviceWebToken and subsequent // AuthenticateDevice call. @@ -171,7 +171,7 @@ type DeviceTrustServiceClient interface { // the external inventory are handled as specified. // Authorized either by a valid MDM service certificate or the appropriate // "device" permissions (create/update/delete). - SyncInventory(ctx context.Context, opts ...grpc.CallOption) (DeviceTrustService_SyncInventoryClient, error) + SyncInventory(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[SyncInventoryRequest, SyncInventoryResponse], error) // Deprecated: Do not use. // Superseded by ResourceUsageService.GetUsage. GetDevicesUsage(ctx context.Context, in *GetDevicesUsageRequest, opts ...grpc.CallOption) (*DevicesUsage, error) @@ -275,69 +275,31 @@ func (c *deviceTrustServiceClient) CreateDeviceEnrollToken(ctx context.Context, return out, nil } -func (c *deviceTrustServiceClient) EnrollDevice(ctx context.Context, opts ...grpc.CallOption) (DeviceTrustService_EnrollDeviceClient, error) { +func (c *deviceTrustServiceClient) EnrollDevice(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[EnrollDeviceRequest, EnrollDeviceResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &DeviceTrustService_ServiceDesc.Streams[0], DeviceTrustService_EnrollDevice_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &deviceTrustServiceEnrollDeviceClient{ClientStream: stream} + x := &grpc.GenericClientStream[EnrollDeviceRequest, EnrollDeviceResponse]{ClientStream: stream} return x, nil } -type DeviceTrustService_EnrollDeviceClient interface { - Send(*EnrollDeviceRequest) error - Recv() (*EnrollDeviceResponse, error) - grpc.ClientStream -} - -type deviceTrustServiceEnrollDeviceClient struct { - grpc.ClientStream -} - -func (x *deviceTrustServiceEnrollDeviceClient) Send(m *EnrollDeviceRequest) error { - return x.ClientStream.SendMsg(m) -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type DeviceTrustService_EnrollDeviceClient = grpc.BidiStreamingClient[EnrollDeviceRequest, EnrollDeviceResponse] -func (x *deviceTrustServiceEnrollDeviceClient) Recv() (*EnrollDeviceResponse, error) { - m := new(EnrollDeviceResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func (c *deviceTrustServiceClient) AuthenticateDevice(ctx context.Context, opts ...grpc.CallOption) (DeviceTrustService_AuthenticateDeviceClient, error) { +func (c *deviceTrustServiceClient) AuthenticateDevice(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[AuthenticateDeviceRequest, AuthenticateDeviceResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &DeviceTrustService_ServiceDesc.Streams[1], DeviceTrustService_AuthenticateDevice_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &deviceTrustServiceAuthenticateDeviceClient{ClientStream: stream} + x := &grpc.GenericClientStream[AuthenticateDeviceRequest, AuthenticateDeviceResponse]{ClientStream: stream} return x, nil } -type DeviceTrustService_AuthenticateDeviceClient interface { - Send(*AuthenticateDeviceRequest) error - Recv() (*AuthenticateDeviceResponse, error) - grpc.ClientStream -} - -type deviceTrustServiceAuthenticateDeviceClient struct { - grpc.ClientStream -} - -func (x *deviceTrustServiceAuthenticateDeviceClient) Send(m *AuthenticateDeviceRequest) error { - return x.ClientStream.SendMsg(m) -} - -func (x *deviceTrustServiceAuthenticateDeviceClient) Recv() (*AuthenticateDeviceResponse, error) { - m := new(AuthenticateDeviceResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type DeviceTrustService_AuthenticateDeviceClient = grpc.BidiStreamingClient[AuthenticateDeviceRequest, AuthenticateDeviceResponse] func (c *deviceTrustServiceClient) ConfirmDeviceWebAuthentication(ctx context.Context, in *ConfirmDeviceWebAuthenticationRequest, opts ...grpc.CallOption) (*ConfirmDeviceWebAuthenticationResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) @@ -349,37 +311,18 @@ func (c *deviceTrustServiceClient) ConfirmDeviceWebAuthentication(ctx context.Co return out, nil } -func (c *deviceTrustServiceClient) SyncInventory(ctx context.Context, opts ...grpc.CallOption) (DeviceTrustService_SyncInventoryClient, error) { +func (c *deviceTrustServiceClient) SyncInventory(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[SyncInventoryRequest, SyncInventoryResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &DeviceTrustService_ServiceDesc.Streams[2], DeviceTrustService_SyncInventory_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &deviceTrustServiceSyncInventoryClient{ClientStream: stream} + x := &grpc.GenericClientStream[SyncInventoryRequest, SyncInventoryResponse]{ClientStream: stream} return x, nil } -type DeviceTrustService_SyncInventoryClient interface { - Send(*SyncInventoryRequest) error - Recv() (*SyncInventoryResponse, error) - grpc.ClientStream -} - -type deviceTrustServiceSyncInventoryClient struct { - grpc.ClientStream -} - -func (x *deviceTrustServiceSyncInventoryClient) Send(m *SyncInventoryRequest) error { - return x.ClientStream.SendMsg(m) -} - -func (x *deviceTrustServiceSyncInventoryClient) Recv() (*SyncInventoryResponse, error) { - m := new(SyncInventoryResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type DeviceTrustService_SyncInventoryClient = grpc.BidiStreamingClient[SyncInventoryRequest, SyncInventoryResponse] // Deprecated: Do not use. func (c *deviceTrustServiceClient) GetDevicesUsage(ctx context.Context, in *GetDevicesUsageRequest, opts ...grpc.CallOption) (*DevicesUsage, error) { @@ -394,7 +337,7 @@ func (c *deviceTrustServiceClient) GetDevicesUsage(ctx context.Context, in *GetD // DeviceTrustServiceServer is the server API for DeviceTrustService service. // All implementations must embed UnimplementedDeviceTrustServiceServer -// for forward compatibility +// for forward compatibility. // // DeviceTrustService provides methods to manage, enroll and authenticate // trusted devices. @@ -483,7 +426,7 @@ type DeviceTrustServiceServer interface { // <- TPMEnrollChallenge (server) // -> TPMEnrollChallengeResponse // <- EnrollDeviceSuccess - EnrollDevice(DeviceTrustService_EnrollDeviceServer) error + EnrollDevice(grpc.BidiStreamingServer[EnrollDeviceRequest, EnrollDeviceResponse]) error // AuthenticateDevice performs the device authentication ceremony. // // Device authentication exchanges existing user certificates without device @@ -491,7 +434,7 @@ type DeviceTrustServiceServer interface { // certificates allow the user to perform device-aware actions. // // Only registered and enrolled devices may perform device authentication. - AuthenticateDevice(DeviceTrustService_AuthenticateDeviceServer) error + AuthenticateDevice(grpc.BidiStreamingServer[AuthenticateDeviceRequest, AuthenticateDeviceResponse]) error // ConfirmDeviceWebAuthentication finalizes the device web authentication // ceremony started by the creation of a DeviceWebToken and subsequent // AuthenticateDevice call. @@ -513,16 +456,19 @@ type DeviceTrustServiceServer interface { // the external inventory are handled as specified. // Authorized either by a valid MDM service certificate or the appropriate // "device" permissions (create/update/delete). - SyncInventory(DeviceTrustService_SyncInventoryServer) error + SyncInventory(grpc.BidiStreamingServer[SyncInventoryRequest, SyncInventoryResponse]) error // Deprecated: Do not use. // Superseded by ResourceUsageService.GetUsage. GetDevicesUsage(context.Context, *GetDevicesUsageRequest) (*DevicesUsage, error) mustEmbedUnimplementedDeviceTrustServiceServer() } -// UnimplementedDeviceTrustServiceServer must be embedded to have forward compatible implementations. -type UnimplementedDeviceTrustServiceServer struct { -} +// UnimplementedDeviceTrustServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedDeviceTrustServiceServer struct{} func (UnimplementedDeviceTrustServiceServer) CreateDevice(context.Context, *CreateDeviceRequest) (*Device, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateDevice not implemented") @@ -551,22 +497,23 @@ func (UnimplementedDeviceTrustServiceServer) BulkCreateDevices(context.Context, func (UnimplementedDeviceTrustServiceServer) CreateDeviceEnrollToken(context.Context, *CreateDeviceEnrollTokenRequest) (*DeviceEnrollToken, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateDeviceEnrollToken not implemented") } -func (UnimplementedDeviceTrustServiceServer) EnrollDevice(DeviceTrustService_EnrollDeviceServer) error { +func (UnimplementedDeviceTrustServiceServer) EnrollDevice(grpc.BidiStreamingServer[EnrollDeviceRequest, EnrollDeviceResponse]) error { return status.Errorf(codes.Unimplemented, "method EnrollDevice not implemented") } -func (UnimplementedDeviceTrustServiceServer) AuthenticateDevice(DeviceTrustService_AuthenticateDeviceServer) error { +func (UnimplementedDeviceTrustServiceServer) AuthenticateDevice(grpc.BidiStreamingServer[AuthenticateDeviceRequest, AuthenticateDeviceResponse]) error { return status.Errorf(codes.Unimplemented, "method AuthenticateDevice not implemented") } func (UnimplementedDeviceTrustServiceServer) ConfirmDeviceWebAuthentication(context.Context, *ConfirmDeviceWebAuthenticationRequest) (*ConfirmDeviceWebAuthenticationResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ConfirmDeviceWebAuthentication not implemented") } -func (UnimplementedDeviceTrustServiceServer) SyncInventory(DeviceTrustService_SyncInventoryServer) error { +func (UnimplementedDeviceTrustServiceServer) SyncInventory(grpc.BidiStreamingServer[SyncInventoryRequest, SyncInventoryResponse]) error { return status.Errorf(codes.Unimplemented, "method SyncInventory not implemented") } func (UnimplementedDeviceTrustServiceServer) GetDevicesUsage(context.Context, *GetDevicesUsageRequest) (*DevicesUsage, error) { return nil, status.Errorf(codes.Unimplemented, "method GetDevicesUsage not implemented") } func (UnimplementedDeviceTrustServiceServer) mustEmbedUnimplementedDeviceTrustServiceServer() {} +func (UnimplementedDeviceTrustServiceServer) testEmbeddedByValue() {} // UnsafeDeviceTrustServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to DeviceTrustServiceServer will @@ -576,6 +523,13 @@ type UnsafeDeviceTrustServiceServer interface { } func RegisterDeviceTrustServiceServer(s grpc.ServiceRegistrar, srv DeviceTrustServiceServer) { + // If the following call pancis, it indicates UnimplementedDeviceTrustServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&DeviceTrustService_ServiceDesc, srv) } @@ -742,56 +696,18 @@ func _DeviceTrustService_CreateDeviceEnrollToken_Handler(srv interface{}, ctx co } func _DeviceTrustService_EnrollDevice_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(DeviceTrustServiceServer).EnrollDevice(&deviceTrustServiceEnrollDeviceServer{ServerStream: stream}) -} - -type DeviceTrustService_EnrollDeviceServer interface { - Send(*EnrollDeviceResponse) error - Recv() (*EnrollDeviceRequest, error) - grpc.ServerStream -} - -type deviceTrustServiceEnrollDeviceServer struct { - grpc.ServerStream -} - -func (x *deviceTrustServiceEnrollDeviceServer) Send(m *EnrollDeviceResponse) error { - return x.ServerStream.SendMsg(m) + return srv.(DeviceTrustServiceServer).EnrollDevice(&grpc.GenericServerStream[EnrollDeviceRequest, EnrollDeviceResponse]{ServerStream: stream}) } -func (x *deviceTrustServiceEnrollDeviceServer) Recv() (*EnrollDeviceRequest, error) { - m := new(EnrollDeviceRequest) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type DeviceTrustService_EnrollDeviceServer = grpc.BidiStreamingServer[EnrollDeviceRequest, EnrollDeviceResponse] func _DeviceTrustService_AuthenticateDevice_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(DeviceTrustServiceServer).AuthenticateDevice(&deviceTrustServiceAuthenticateDeviceServer{ServerStream: stream}) -} - -type DeviceTrustService_AuthenticateDeviceServer interface { - Send(*AuthenticateDeviceResponse) error - Recv() (*AuthenticateDeviceRequest, error) - grpc.ServerStream -} - -type deviceTrustServiceAuthenticateDeviceServer struct { - grpc.ServerStream + return srv.(DeviceTrustServiceServer).AuthenticateDevice(&grpc.GenericServerStream[AuthenticateDeviceRequest, AuthenticateDeviceResponse]{ServerStream: stream}) } -func (x *deviceTrustServiceAuthenticateDeviceServer) Send(m *AuthenticateDeviceResponse) error { - return x.ServerStream.SendMsg(m) -} - -func (x *deviceTrustServiceAuthenticateDeviceServer) Recv() (*AuthenticateDeviceRequest, error) { - m := new(AuthenticateDeviceRequest) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type DeviceTrustService_AuthenticateDeviceServer = grpc.BidiStreamingServer[AuthenticateDeviceRequest, AuthenticateDeviceResponse] func _DeviceTrustService_ConfirmDeviceWebAuthentication_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ConfirmDeviceWebAuthenticationRequest) @@ -812,30 +728,11 @@ func _DeviceTrustService_ConfirmDeviceWebAuthentication_Handler(srv interface{}, } func _DeviceTrustService_SyncInventory_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(DeviceTrustServiceServer).SyncInventory(&deviceTrustServiceSyncInventoryServer{ServerStream: stream}) -} - -type DeviceTrustService_SyncInventoryServer interface { - Send(*SyncInventoryResponse) error - Recv() (*SyncInventoryRequest, error) - grpc.ServerStream + return srv.(DeviceTrustServiceServer).SyncInventory(&grpc.GenericServerStream[SyncInventoryRequest, SyncInventoryResponse]{ServerStream: stream}) } -type deviceTrustServiceSyncInventoryServer struct { - grpc.ServerStream -} - -func (x *deviceTrustServiceSyncInventoryServer) Send(m *SyncInventoryResponse) error { - return x.ServerStream.SendMsg(m) -} - -func (x *deviceTrustServiceSyncInventoryServer) Recv() (*SyncInventoryRequest, error) { - m := new(SyncInventoryRequest) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type DeviceTrustService_SyncInventoryServer = grpc.BidiStreamingServer[SyncInventoryRequest, SyncInventoryResponse] func _DeviceTrustService_GetDevicesUsage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetDevicesUsageRequest) diff --git a/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig_service_grpc.pb.go b/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig_service_grpc.pb.go index 931e454d0d742..aec7605c5ed80 100644 --- a/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/discoveryconfig/v1/discoveryconfig_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( DiscoveryConfigService_ListDiscoveryConfigs_FullMethodName = "/teleport.discoveryconfig.v1.DiscoveryConfigService/ListDiscoveryConfigs" @@ -162,7 +162,7 @@ func (c *discoveryConfigServiceClient) UpdateDiscoveryConfigStatus(ctx context.C // DiscoveryConfigServiceServer is the server API for DiscoveryConfigService service. // All implementations must embed UnimplementedDiscoveryConfigServiceServer -// for forward compatibility +// for forward compatibility. // // DiscoveryConfigService provides methods to manage Discovery Configs. // @@ -189,9 +189,12 @@ type DiscoveryConfigServiceServer interface { mustEmbedUnimplementedDiscoveryConfigServiceServer() } -// UnimplementedDiscoveryConfigServiceServer must be embedded to have forward compatible implementations. -type UnimplementedDiscoveryConfigServiceServer struct { -} +// UnimplementedDiscoveryConfigServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedDiscoveryConfigServiceServer struct{} func (UnimplementedDiscoveryConfigServiceServer) ListDiscoveryConfigs(context.Context, *ListDiscoveryConfigsRequest) (*ListDiscoveryConfigsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListDiscoveryConfigs not implemented") @@ -219,6 +222,7 @@ func (UnimplementedDiscoveryConfigServiceServer) UpdateDiscoveryConfigStatus(con } func (UnimplementedDiscoveryConfigServiceServer) mustEmbedUnimplementedDiscoveryConfigServiceServer() { } +func (UnimplementedDiscoveryConfigServiceServer) testEmbeddedByValue() {} // UnsafeDiscoveryConfigServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to DiscoveryConfigServiceServer will @@ -228,6 +232,13 @@ type UnsafeDiscoveryConfigServiceServer interface { } func RegisterDiscoveryConfigServiceServer(s grpc.ServiceRegistrar, srv DiscoveryConfigServiceServer) { + // If the following call pancis, it indicates UnimplementedDiscoveryConfigServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&DiscoveryConfigService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/externalauditstorage/v1/externalauditstorage_service_grpc.pb.go b/api/gen/proto/go/teleport/externalauditstorage/v1/externalauditstorage_service_grpc.pb.go index 4daf16e04b176..ae7b61b886d61 100644 --- a/api/gen/proto/go/teleport/externalauditstorage/v1/externalauditstorage_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/externalauditstorage/v1/externalauditstorage_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/externalauditstorage/v1/externalauditstorage_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( ExternalAuditStorageService_GetDraftExternalAuditStorage_FullMethodName = "/teleport.externalauditstorage.v1.ExternalAuditStorageService/GetDraftExternalAuditStorage" @@ -214,7 +214,7 @@ func (c *externalAuditStorageServiceClient) TestDraftExternalAuditStorageAthena( // ExternalAuditStorageServiceServer is the server API for ExternalAuditStorageService service. // All implementations must embed UnimplementedExternalAuditStorageServiceServer -// for forward compatibility +// for forward compatibility. // // ExternalAuditStorageService provides methods to manage External Audit Storage. // @@ -260,9 +260,12 @@ type ExternalAuditStorageServiceServer interface { mustEmbedUnimplementedExternalAuditStorageServiceServer() } -// UnimplementedExternalAuditStorageServiceServer must be embedded to have forward compatible implementations. -type UnimplementedExternalAuditStorageServiceServer struct { -} +// UnimplementedExternalAuditStorageServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedExternalAuditStorageServiceServer struct{} func (UnimplementedExternalAuditStorageServiceServer) GetDraftExternalAuditStorage(context.Context, *GetDraftExternalAuditStorageRequest) (*GetDraftExternalAuditStorageResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetDraftExternalAuditStorage not implemented") @@ -299,6 +302,7 @@ func (UnimplementedExternalAuditStorageServiceServer) TestDraftExternalAuditStor } func (UnimplementedExternalAuditStorageServiceServer) mustEmbedUnimplementedExternalAuditStorageServiceServer() { } +func (UnimplementedExternalAuditStorageServiceServer) testEmbeddedByValue() {} // UnsafeExternalAuditStorageServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to ExternalAuditStorageServiceServer will @@ -308,6 +312,13 @@ type UnsafeExternalAuditStorageServiceServer interface { } func RegisterExternalAuditStorageServiceServer(s grpc.ServiceRegistrar, srv ExternalAuditStorageServiceServer) { + // If the following call pancis, it indicates UnimplementedExternalAuditStorageServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&ExternalAuditStorageService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/integration/v1/awsoidc_service.pb.go b/api/gen/proto/go/teleport/integration/v1/awsoidc_service.pb.go index 91041b5503b53..786bbe9f19097 100644 --- a/api/gen/proto/go/teleport/integration/v1/awsoidc_service.pb.go +++ b/api/gen/proto/go/teleport/integration/v1/awsoidc_service.pb.go @@ -1085,7 +1085,7 @@ type Subnet struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Name is the subnet name. + // Name is the subnet name. Can be empty. Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // ID is the subnet ID. Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` @@ -1205,6 +1205,193 @@ func (x *ListSubnetsResponse) GetNextToken() string { return "" } +// ListVPCsRequest is a request for a paginated list of AWS VPCs. +type ListVPCsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Integration is the AWS OIDC Integration name. + // Required. + Integration string `protobuf:"bytes,1,opt,name=integration,proto3" json:"integration,omitempty"` + // Region is the AWS Region + // Required. + Region string `protobuf:"bytes,2,opt,name=region,proto3" json:"region,omitempty"` + // NextToken is the token to be used to fetch the next page. + // If empty, the first page is fetched. + NextToken string `protobuf:"bytes,3,opt,name=next_token,json=nextToken,proto3" json:"next_token,omitempty"` +} + +func (x *ListVPCsRequest) Reset() { + *x = ListVPCsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListVPCsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListVPCsRequest) ProtoMessage() {} + +func (x *ListVPCsRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListVPCsRequest.ProtoReflect.Descriptor instead. +func (*ListVPCsRequest) Descriptor() ([]byte, []int) { + return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{16} +} + +func (x *ListVPCsRequest) GetIntegration() string { + if x != nil { + return x.Integration + } + return "" +} + +func (x *ListVPCsRequest) GetRegion() string { + if x != nil { + return x.Region + } + return "" +} + +func (x *ListVPCsRequest) GetNextToken() string { + if x != nil { + return x.NextToken + } + return "" +} + +// VPC is a representation of an AWS VPC. +type VPC struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Name is the VPC name. Can be empty. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // ID is the VPC ID. + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *VPC) Reset() { + *x = VPC{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *VPC) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VPC) ProtoMessage() {} + +func (x *VPC) ProtoReflect() protoreflect.Message { + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VPC.ProtoReflect.Descriptor instead. +func (*VPC) Descriptor() ([]byte, []int) { + return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{17} +} + +func (x *VPC) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *VPC) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +// ListVPCsResponse contains a page of AWS VPCs. +type ListVPCsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // VPCs contains the page of VPCs. + Vpcs []*VPC `protobuf:"bytes,1,rep,name=vpcs,proto3" json:"vpcs,omitempty"` + // NextToken is used for pagination. + // If non-empty, it can be used to request the next page. + NextToken string `protobuf:"bytes,2,opt,name=next_token,json=nextToken,proto3" json:"next_token,omitempty"` +} + +func (x *ListVPCsResponse) Reset() { + *x = ListVPCsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListVPCsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListVPCsResponse) ProtoMessage() {} + +func (x *ListVPCsResponse) ProtoReflect() protoreflect.Message { + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListVPCsResponse.ProtoReflect.Descriptor instead. +func (*ListVPCsResponse) Descriptor() ([]byte, []int) { + return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{18} +} + +func (x *ListVPCsResponse) GetVpcs() []*VPC { + if x != nil { + return x.Vpcs + } + return nil +} + +func (x *ListVPCsResponse) GetNextToken() string { + if x != nil { + return x.NextToken + } + return "" +} + // DeployDatabaseServiceRequest is a request to deploy . type DeployDatabaseServiceRequest struct { state protoimpl.MessageState @@ -1236,7 +1423,7 @@ type DeployDatabaseServiceRequest struct { func (x *DeployDatabaseServiceRequest) Reset() { *x = DeployDatabaseServiceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[16] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1249,7 +1436,7 @@ func (x *DeployDatabaseServiceRequest) String() string { func (*DeployDatabaseServiceRequest) ProtoMessage() {} func (x *DeployDatabaseServiceRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[16] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1262,7 +1449,7 @@ func (x *DeployDatabaseServiceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeployDatabaseServiceRequest.ProtoReflect.Descriptor instead. func (*DeployDatabaseServiceRequest) Descriptor() ([]byte, []int) { - return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{16} + return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{19} } func (x *DeployDatabaseServiceRequest) GetIntegration() string { @@ -1331,7 +1518,7 @@ type DeployDatabaseServiceDeployment struct { func (x *DeployDatabaseServiceDeployment) Reset() { *x = DeployDatabaseServiceDeployment{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[17] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1344,7 +1531,7 @@ func (x *DeployDatabaseServiceDeployment) String() string { func (*DeployDatabaseServiceDeployment) ProtoMessage() {} func (x *DeployDatabaseServiceDeployment) ProtoReflect() protoreflect.Message { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[17] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1357,7 +1544,7 @@ func (x *DeployDatabaseServiceDeployment) ProtoReflect() protoreflect.Message { // Deprecated: Use DeployDatabaseServiceDeployment.ProtoReflect.Descriptor instead. func (*DeployDatabaseServiceDeployment) Descriptor() ([]byte, []int) { - return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{17} + return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{20} } func (x *DeployDatabaseServiceDeployment) GetTeleportConfigString() string { @@ -1403,7 +1590,7 @@ type DeployDatabaseServiceResponse struct { func (x *DeployDatabaseServiceResponse) Reset() { *x = DeployDatabaseServiceResponse{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[18] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1416,7 +1603,7 @@ func (x *DeployDatabaseServiceResponse) String() string { func (*DeployDatabaseServiceResponse) ProtoMessage() {} func (x *DeployDatabaseServiceResponse) ProtoReflect() protoreflect.Message { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[18] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1429,7 +1616,7 @@ func (x *DeployDatabaseServiceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeployDatabaseServiceResponse.ProtoReflect.Descriptor instead. func (*DeployDatabaseServiceResponse) Descriptor() ([]byte, []int) { - return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{18} + return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{21} } func (x *DeployDatabaseServiceResponse) GetClusterArn() string { @@ -1488,7 +1675,7 @@ type DeployServiceRequest struct { func (x *DeployServiceRequest) Reset() { *x = DeployServiceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[19] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1501,7 +1688,7 @@ func (x *DeployServiceRequest) String() string { func (*DeployServiceRequest) ProtoMessage() {} func (x *DeployServiceRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[19] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1514,7 +1701,7 @@ func (x *DeployServiceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeployServiceRequest.ProtoReflect.Descriptor instead. func (*DeployServiceRequest) Descriptor() ([]byte, []int) { - return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{19} + return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{22} } func (x *DeployServiceRequest) GetIntegration() string { @@ -1599,7 +1786,7 @@ type DeployServiceResponse struct { func (x *DeployServiceResponse) Reset() { *x = DeployServiceResponse{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[20] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1612,7 +1799,7 @@ func (x *DeployServiceResponse) String() string { func (*DeployServiceResponse) ProtoMessage() {} func (x *DeployServiceResponse) ProtoReflect() protoreflect.Message { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[20] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1625,7 +1812,7 @@ func (x *DeployServiceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeployServiceResponse.ProtoReflect.Descriptor instead. func (*DeployServiceResponse) Descriptor() ([]byte, []int) { - return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{20} + return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{23} } func (x *DeployServiceResponse) GetClusterArn() string { @@ -1681,7 +1868,7 @@ type EnrollEKSClustersRequest struct { func (x *EnrollEKSClustersRequest) Reset() { *x = EnrollEKSClustersRequest{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[21] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1694,7 +1881,7 @@ func (x *EnrollEKSClustersRequest) String() string { func (*EnrollEKSClustersRequest) ProtoMessage() {} func (x *EnrollEKSClustersRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[21] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1707,7 +1894,7 @@ func (x *EnrollEKSClustersRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use EnrollEKSClustersRequest.ProtoReflect.Descriptor instead. func (*EnrollEKSClustersRequest) Descriptor() ([]byte, []int) { - return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{21} + return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{24} } func (x *EnrollEKSClustersRequest) GetIntegration() string { @@ -1762,7 +1949,7 @@ type EnrollEKSClusterResult struct { func (x *EnrollEKSClusterResult) Reset() { *x = EnrollEKSClusterResult{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[22] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1775,7 +1962,7 @@ func (x *EnrollEKSClusterResult) String() string { func (*EnrollEKSClusterResult) ProtoMessage() {} func (x *EnrollEKSClusterResult) ProtoReflect() protoreflect.Message { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[22] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1788,7 +1975,7 @@ func (x *EnrollEKSClusterResult) ProtoReflect() protoreflect.Message { // Deprecated: Use EnrollEKSClusterResult.ProtoReflect.Descriptor instead. func (*EnrollEKSClusterResult) Descriptor() ([]byte, []int) { - return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{22} + return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{25} } func (x *EnrollEKSClusterResult) GetEksClusterName() string { @@ -1825,7 +2012,7 @@ type EnrollEKSClustersResponse struct { func (x *EnrollEKSClustersResponse) Reset() { *x = EnrollEKSClustersResponse{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[23] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1838,7 +2025,7 @@ func (x *EnrollEKSClustersResponse) String() string { func (*EnrollEKSClustersResponse) ProtoMessage() {} func (x *EnrollEKSClustersResponse) ProtoReflect() protoreflect.Message { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[23] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1851,7 +2038,7 @@ func (x *EnrollEKSClustersResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use EnrollEKSClustersResponse.ProtoReflect.Descriptor instead. func (*EnrollEKSClustersResponse) Descriptor() ([]byte, []int) { - return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{23} + return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{26} } func (x *EnrollEKSClustersResponse) GetResults() []*EnrollEKSClusterResult { @@ -1881,7 +2068,7 @@ type ListEC2Request struct { func (x *ListEC2Request) Reset() { *x = ListEC2Request{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[24] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1894,7 +2081,7 @@ func (x *ListEC2Request) String() string { func (*ListEC2Request) ProtoMessage() {} func (x *ListEC2Request) ProtoReflect() protoreflect.Message { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[24] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1907,7 +2094,7 @@ func (x *ListEC2Request) ProtoReflect() protoreflect.Message { // Deprecated: Use ListEC2Request.ProtoReflect.Descriptor instead. func (*ListEC2Request) Descriptor() ([]byte, []int) { - return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{24} + return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{27} } func (x *ListEC2Request) GetIntegration() string { @@ -1947,7 +2134,7 @@ type ListEC2Response struct { func (x *ListEC2Response) Reset() { *x = ListEC2Response{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[25] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1960,7 +2147,7 @@ func (x *ListEC2Response) String() string { func (*ListEC2Response) ProtoMessage() {} func (x *ListEC2Response) ProtoReflect() protoreflect.Message { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[25] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1973,7 +2160,7 @@ func (x *ListEC2Response) ProtoReflect() protoreflect.Message { // Deprecated: Use ListEC2Response.ProtoReflect.Descriptor instead. func (*ListEC2Response) Descriptor() ([]byte, []int) { - return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{25} + return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{28} } func (x *ListEC2Response) GetServers() []*types.ServerV2 { @@ -2010,7 +2197,7 @@ type ListEKSClustersRequest struct { func (x *ListEKSClustersRequest) Reset() { *x = ListEKSClustersRequest{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[26] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2023,7 +2210,7 @@ func (x *ListEKSClustersRequest) String() string { func (*ListEKSClustersRequest) ProtoMessage() {} func (x *ListEKSClustersRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[26] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2036,7 +2223,7 @@ func (x *ListEKSClustersRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListEKSClustersRequest.ProtoReflect.Descriptor instead. func (*ListEKSClustersRequest) Descriptor() ([]byte, []int) { - return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{26} + return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{29} } func (x *ListEKSClustersRequest) GetIntegration() string { @@ -2086,7 +2273,7 @@ type EKSCluster struct { func (x *EKSCluster) Reset() { *x = EKSCluster{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[27] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2099,7 +2286,7 @@ func (x *EKSCluster) String() string { func (*EKSCluster) ProtoMessage() {} func (x *EKSCluster) ProtoReflect() protoreflect.Message { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[27] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2112,7 +2299,7 @@ func (x *EKSCluster) ProtoReflect() protoreflect.Message { // Deprecated: Use EKSCluster.ProtoReflect.Descriptor instead. func (*EKSCluster) Descriptor() ([]byte, []int) { - return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{27} + return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{30} } func (x *EKSCluster) GetName() string { @@ -2173,7 +2360,7 @@ type ListEKSClustersResponse struct { func (x *ListEKSClustersResponse) Reset() { *x = ListEKSClustersResponse{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[28] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2186,7 +2373,7 @@ func (x *ListEKSClustersResponse) String() string { func (*ListEKSClustersResponse) ProtoMessage() {} func (x *ListEKSClustersResponse) ProtoReflect() protoreflect.Message { - mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[28] + mi := &file_teleport_integration_v1_awsoidc_service_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2199,7 +2386,7 @@ func (x *ListEKSClustersResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListEKSClustersResponse.ProtoReflect.Descriptor instead. func (*ListEKSClustersResponse) Descriptor() ([]byte, []int) { - return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{28} + return file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP(), []int{31} } func (x *ListEKSClustersResponse) GetClusters() []*EKSCluster { @@ -2370,240 +2557,262 @@ var file_teleport_integration_v1_awsoidc_service_proto_rawDesc = []byte{ 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x65, 0x78, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xc0, - 0x02, 0x0a, 0x1c, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, - 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x20, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x0a, 0x0d, 0x74, 0x61, 0x73, - 0x6b, 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x61, 0x72, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x74, 0x61, 0x73, 0x6b, 0x52, 0x6f, 0x6c, 0x65, 0x41, 0x72, 0x6e, 0x12, 0x29, 0x0a, - 0x10, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3b, 0x0a, 0x1a, 0x64, 0x65, 0x70, 0x6c, - 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, - 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x64, 0x65, - 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4a, 0x6f, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x5a, 0x0a, 0x0b, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x44, 0x61, 0x74, 0x61, 0x62, - 0x61, 0x73, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0b, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x73, 0x22, 0xb6, 0x01, 0x0a, 0x1f, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x44, 0x61, 0x74, 0x61, - 0x62, 0x61, 0x73, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x70, 0x6c, 0x6f, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x15, 0x0a, 0x06, 0x76, - 0x70, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x70, 0x63, - 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x49, 0x64, - 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x65, 0x63, 0x75, - 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x22, 0x74, 0x0a, 0x1d, 0x44, 0x65, - 0x70, 0x6c, 0x6f, 0x79, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x41, 0x72, 0x6e, 0x12, 0x32, 0x0a, 0x15, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x64, 0x61, 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, - 0x64, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x44, 0x61, 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x55, 0x72, 0x6c, - 0x22, 0x83, 0x03, 0x0a, 0x14, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x6e, 0x74, - 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, - 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, - 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x65, - 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x27, 0x0a, 0x0f, - 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x5f, - 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x73, 0x75, 0x62, 0x6e, 0x65, - 0x74, 0x49, 0x64, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x72, 0x6f, 0x6c, - 0x65, 0x5f, 0x61, 0x72, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, 0x73, - 0x6b, 0x52, 0x6f, 0x6c, 0x65, 0x41, 0x72, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x65, 0x6c, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x3b, 0x0a, 0x1a, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x4a, 0x6f, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x34, 0x0a, 0x16, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x14, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0xbd, 0x01, 0x0a, 0x15, 0x44, 0x65, 0x70, 0x6c, 0x6f, - 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x6e, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x41, 0x72, - 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x72, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, - 0x72, 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x72, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x11, 0x74, 0x61, 0x73, 0x6b, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x41, - 0x72, 0x6e, 0x12, 0x32, 0x0a, 0x15, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x64, 0x61, - 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x13, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x73, 0x68, 0x62, 0x6f, - 0x61, 0x72, 0x64, 0x55, 0x72, 0x6c, 0x22, 0xd7, 0x01, 0x0a, 0x18, 0x45, 0x6e, 0x72, 0x6f, 0x6c, - 0x6c, 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, - 0x11, 0x65, 0x6b, 0x73, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x65, 0x6b, 0x73, 0x43, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x70, 0x70, 0x5f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, - 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, - 0x70, 0x70, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0c, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x22, 0x79, 0x0a, 0x16, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x65, 0x6b, - 0x73, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x6b, 0x73, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x66, 0x0a, 0x19, 0x45, - 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x73, 0x22, 0x69, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x43, 0x32, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, - 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, - 0x1d, 0x0a, 0x0a, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x65, 0x78, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x5b, - 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x43, 0x32, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x56, 0x32, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x1d, 0x0a, 0x0a, - 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x6e, 0x65, 0x78, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x71, 0x0a, 0x16, 0x4c, - 0x69, 0x73, 0x74, 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x65, 0x78, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x6a, + 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x50, 0x43, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, + 0x65, 0x78, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x6e, 0x65, 0x78, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x29, 0x0a, 0x03, 0x56, 0x50, + 0x43, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x63, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x50, 0x43, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x04, 0x76, 0x70, 0x63, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, + 0x31, 0x2e, 0x56, 0x50, 0x43, 0x52, 0x04, 0x76, 0x70, 0x63, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, + 0x65, 0x78, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x6e, 0x65, 0x78, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xc0, 0x02, 0x0a, 0x1c, 0x44, + 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x69, + 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, + 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, + 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x0a, 0x0d, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x72, 0x6f, + 0x6c, 0x65, 0x5f, 0x61, 0x72, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, + 0x73, 0x6b, 0x52, 0x6f, 0x6c, 0x65, 0x41, 0x72, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3b, 0x0a, 0x1a, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x4a, 0x6f, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x5a, 0x0a, 0x0b, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, + 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x52, 0x0b, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xb6, 0x01, + 0x0a, 0x1f, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x12, 0x34, 0x0a, 0x16, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x14, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x15, 0x0a, 0x06, 0x76, 0x70, 0x63, 0x5f, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x70, 0x63, 0x49, 0x64, 0x12, 0x1d, + 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x09, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x49, 0x64, 0x73, 0x12, 0x27, 0x0a, + 0x0f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x22, 0x74, 0x0a, 0x1d, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, + 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x5f, 0x61, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x41, 0x72, 0x6e, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x5f, 0x64, 0x61, 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x5f, 0x75, 0x72, + 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x44, 0x61, 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x55, 0x72, 0x6c, 0x22, 0x83, 0x03, 0x0a, + 0x14, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, - 0x1d, 0x0a, 0x0a, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x65, 0x78, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xfb, - 0x02, 0x0a, 0x0a, 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x72, 0x6e, 0x12, 0x47, 0x0a, 0x06, 0x6c, - 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, - 0x62, 0x65, 0x6c, 0x73, 0x12, 0x54, 0x0a, 0x0b, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x6c, 0x61, 0x62, - 0x65, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x4a, - 0x6f, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, - 0x6a, 0x6f, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3d, 0x0a, - 0x0f, 0x4a, 0x6f, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x79, 0x0a, 0x17, - 0x4c, 0x69, 0x73, 0x74, 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x08, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x65, 0x78, 0x74, + 0x27, 0x0a, 0x0f, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x6f, + 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x65, 0x63, 0x75, + 0x72, 0x69, 0x74, 0x79, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x49, 0x64, 0x73, + 0x12, 0x22, 0x0a, 0x0d, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x61, 0x72, + 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, 0x73, 0x6b, 0x52, 0x6f, 0x6c, + 0x65, 0x41, 0x72, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x3b, 0x0a, 0x1a, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6a, 0x6f, + 0x69, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x17, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4a, + 0x6f, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x34, 0x0a, 0x16, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, + 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x22, 0xbd, 0x01, 0x0a, 0x15, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x41, 0x72, 0x6e, 0x12, 0x1f, 0x0a, + 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x72, 0x6e, 0x12, 0x2e, + 0x0a, 0x13, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x61, 0x72, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x74, 0x61, 0x73, + 0x6b, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x6e, 0x12, 0x32, + 0x0a, 0x15, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x73, 0x68, 0x62, 0x6f, + 0x61, 0x72, 0x64, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x55, + 0x72, 0x6c, 0x22, 0xd7, 0x01, 0x0a, 0x18, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x45, 0x4b, 0x53, + 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x20, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x11, 0x65, 0x6b, 0x73, + 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x65, 0x6b, 0x73, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, + 0x61, 0x70, 0x70, 0x5f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x12, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x70, 0x70, 0x44, 0x69, + 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x67, 0x65, 0x6e, 0x74, + 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x61, 0x67, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x79, 0x0a, 0x16, + 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x65, 0x6b, 0x73, 0x5f, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0e, 0x65, 0x6b, 0x73, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, + 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x66, 0x0a, 0x19, 0x45, 0x6e, 0x72, 0x6f, 0x6c, + 0x6c, 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, + 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, + 0x69, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x43, 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, + 0x65, 0x78, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x6e, 0x65, 0x78, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x5b, 0x0a, 0x0f, 0x4c, 0x69, + 0x73, 0x74, 0x45, 0x43, 0x32, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, + 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, + 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x56, 0x32, 0x52, + 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x65, - 0x78, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x32, 0xfa, 0x08, 0x0a, 0x0e, 0x41, 0x57, 0x53, 0x4f, - 0x49, 0x44, 0x43, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5f, 0x0a, 0x08, 0x4c, 0x69, - 0x73, 0x74, 0x45, 0x49, 0x43, 0x45, 0x12, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x49, 0x43, 0x45, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, - 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, - 0x49, 0x43, 0x45, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x0a, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x49, 0x43, 0x45, 0x12, 0x2a, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x49, 0x43, 0x45, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x78, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x71, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x45, + 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, + 0x65, 0x78, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x6e, 0x65, 0x78, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xfb, 0x02, 0x0a, 0x0a, 0x45, + 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, + 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x61, 0x72, 0x6e, 0x12, 0x47, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, + 0x31, 0x2e, 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x4c, 0x61, 0x62, + 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, + 0x12, 0x54, 0x0a, 0x0b, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, + 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x49, 0x43, 0x45, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x6e, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, - 0x73, 0x65, 0x73, 0x12, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, - 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, + 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x4c, + 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x6a, 0x6f, 0x69, 0x6e, + 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, 0x39, + 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3d, 0x0a, 0x0f, 0x4a, 0x6f, 0x69, + 0x6e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x79, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, + 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, + 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x08, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x65, 0x78, 0x74, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x32, 0xdb, 0x09, 0x0a, 0x0e, 0x41, 0x57, 0x53, 0x4f, 0x49, 0x44, 0x43, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5f, 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x49, + 0x43, 0x45, 0x12, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x7d, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, - 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x32, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x74, 0x45, 0x49, 0x43, 0x45, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x49, 0x43, 0x45, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x45, 0x49, 0x43, 0x45, 0x12, 0x2a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x49, 0x43, 0x45, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, + 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x45, 0x49, 0x43, 0x45, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6e, + 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x12, + 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x61, + 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x61, 0x74, + 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7d, + 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x73, 0x12, 0x32, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x74, + 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x68, 0x0a, + 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x73, 0x12, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x75, 0x72, - 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x68, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x73, - 0x12, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, - 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, - 0x75, 0x62, 0x6e, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, + 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x6e, 0x65, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x56, + 0x50, 0x43, 0x73, 0x12, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, + 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x56, 0x50, 0x43, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x6e, - 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x86, 0x01, 0x0a, 0x15, - 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x35, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, - 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x74, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x44, 0x61, 0x74, - 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6e, 0x0a, 0x0d, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, - 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, - 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x44, - 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7a, 0x0a, 0x11, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x45, 0x4b, - 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x50, 0x43, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x86, 0x01, 0x0a, 0x15, 0x44, 0x65, 0x70, + 0x6c, 0x6f, 0x79, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x12, 0x35, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, + 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, + 0x6c, 0x6f, 0x79, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, + 0x73, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x6e, 0x0a, 0x0d, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x12, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, + 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, + 0x6c, 0x6f, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, + 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6c, + 0x6f, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x7a, 0x0a, 0x11, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x45, 0x4b, 0x53, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, + 0x2e, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x74, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x45, 0x4b, 0x53, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x5c, 0x0a, 0x07, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x43, 0x32, 0x12, 0x27, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x43, 0x32, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, - 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x45, 0x43, 0x32, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x74, - 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x73, 0x12, 0x2f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, + 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, + 0x07, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x43, 0x32, 0x12, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, + 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x43, 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, - 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x5a, 0x5a, 0x58, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, - 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, - 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, - 0x76, 0x31, 0x3b, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x76, 0x31, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x45, 0x43, 0x32, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x74, 0x0a, 0x0f, 0x4c, + 0x69, 0x73, 0x74, 0x45, 0x4b, 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2f, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x4b, 0x53, + 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x30, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x67, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x4b, + 0x53, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x42, 0x5a, 0x5a, 0x58, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x3b, + 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x76, 0x31, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2618,7 +2827,7 @@ func file_teleport_integration_v1_awsoidc_service_proto_rawDescGZIP() []byte { return file_teleport_integration_v1_awsoidc_service_proto_rawDescData } -var file_teleport_integration_v1_awsoidc_service_proto_msgTypes = make([]protoimpl.MessageInfo, 31) +var file_teleport_integration_v1_awsoidc_service_proto_msgTypes = make([]protoimpl.MessageInfo, 34) var file_teleport_integration_v1_awsoidc_service_proto_goTypes = []any{ (*ListEICERequest)(nil), // 0: teleport.integration.v1.ListEICERequest (*EC2InstanceConnectEndpoint)(nil), // 1: teleport.integration.v1.EC2InstanceConnectEndpoint @@ -2636,65 +2845,71 @@ var file_teleport_integration_v1_awsoidc_service_proto_goTypes = []any{ (*ListSubnetsRequest)(nil), // 13: teleport.integration.v1.ListSubnetsRequest (*Subnet)(nil), // 14: teleport.integration.v1.Subnet (*ListSubnetsResponse)(nil), // 15: teleport.integration.v1.ListSubnetsResponse - (*DeployDatabaseServiceRequest)(nil), // 16: teleport.integration.v1.DeployDatabaseServiceRequest - (*DeployDatabaseServiceDeployment)(nil), // 17: teleport.integration.v1.DeployDatabaseServiceDeployment - (*DeployDatabaseServiceResponse)(nil), // 18: teleport.integration.v1.DeployDatabaseServiceResponse - (*DeployServiceRequest)(nil), // 19: teleport.integration.v1.DeployServiceRequest - (*DeployServiceResponse)(nil), // 20: teleport.integration.v1.DeployServiceResponse - (*EnrollEKSClustersRequest)(nil), // 21: teleport.integration.v1.EnrollEKSClustersRequest - (*EnrollEKSClusterResult)(nil), // 22: teleport.integration.v1.EnrollEKSClusterResult - (*EnrollEKSClustersResponse)(nil), // 23: teleport.integration.v1.EnrollEKSClustersResponse - (*ListEC2Request)(nil), // 24: teleport.integration.v1.ListEC2Request - (*ListEC2Response)(nil), // 25: teleport.integration.v1.ListEC2Response - (*ListEKSClustersRequest)(nil), // 26: teleport.integration.v1.ListEKSClustersRequest - (*EKSCluster)(nil), // 27: teleport.integration.v1.EKSCluster - (*ListEKSClustersResponse)(nil), // 28: teleport.integration.v1.ListEKSClustersResponse - nil, // 29: teleport.integration.v1.EKSCluster.LabelsEntry - nil, // 30: teleport.integration.v1.EKSCluster.JoinLabelsEntry - (*types.DatabaseV3)(nil), // 31: types.DatabaseV3 - (*types.ServerV2)(nil), // 32: types.ServerV2 + (*ListVPCsRequest)(nil), // 16: teleport.integration.v1.ListVPCsRequest + (*VPC)(nil), // 17: teleport.integration.v1.VPC + (*ListVPCsResponse)(nil), // 18: teleport.integration.v1.ListVPCsResponse + (*DeployDatabaseServiceRequest)(nil), // 19: teleport.integration.v1.DeployDatabaseServiceRequest + (*DeployDatabaseServiceDeployment)(nil), // 20: teleport.integration.v1.DeployDatabaseServiceDeployment + (*DeployDatabaseServiceResponse)(nil), // 21: teleport.integration.v1.DeployDatabaseServiceResponse + (*DeployServiceRequest)(nil), // 22: teleport.integration.v1.DeployServiceRequest + (*DeployServiceResponse)(nil), // 23: teleport.integration.v1.DeployServiceResponse + (*EnrollEKSClustersRequest)(nil), // 24: teleport.integration.v1.EnrollEKSClustersRequest + (*EnrollEKSClusterResult)(nil), // 25: teleport.integration.v1.EnrollEKSClusterResult + (*EnrollEKSClustersResponse)(nil), // 26: teleport.integration.v1.EnrollEKSClustersResponse + (*ListEC2Request)(nil), // 27: teleport.integration.v1.ListEC2Request + (*ListEC2Response)(nil), // 28: teleport.integration.v1.ListEC2Response + (*ListEKSClustersRequest)(nil), // 29: teleport.integration.v1.ListEKSClustersRequest + (*EKSCluster)(nil), // 30: teleport.integration.v1.EKSCluster + (*ListEKSClustersResponse)(nil), // 31: teleport.integration.v1.ListEKSClustersResponse + nil, // 32: teleport.integration.v1.EKSCluster.LabelsEntry + nil, // 33: teleport.integration.v1.EKSCluster.JoinLabelsEntry + (*types.DatabaseV3)(nil), // 34: types.DatabaseV3 + (*types.ServerV2)(nil), // 35: types.ServerV2 } var file_teleport_integration_v1_awsoidc_service_proto_depIdxs = []int32{ 1, // 0: teleport.integration.v1.ListEICEResponse.ec2ices:type_name -> teleport.integration.v1.EC2InstanceConnectEndpoint 4, // 1: teleport.integration.v1.CreateEICERequest.endpoints:type_name -> teleport.integration.v1.EC2ICEndpoint 4, // 2: teleport.integration.v1.CreateEICEResponse.created_endpoints:type_name -> teleport.integration.v1.EC2ICEndpoint - 31, // 3: teleport.integration.v1.ListDatabasesResponse.databases:type_name -> types.DatabaseV3 + 34, // 3: teleport.integration.v1.ListDatabasesResponse.databases:type_name -> types.DatabaseV3 9, // 4: teleport.integration.v1.SecurityGroupRule.cidrs:type_name -> teleport.integration.v1.SecurityGroupRuleCIDR 10, // 5: teleport.integration.v1.SecurityGroup.inbound_rules:type_name -> teleport.integration.v1.SecurityGroupRule 10, // 6: teleport.integration.v1.SecurityGroup.outbound_rules:type_name -> teleport.integration.v1.SecurityGroupRule 11, // 7: teleport.integration.v1.ListSecurityGroupsResponse.security_groups:type_name -> teleport.integration.v1.SecurityGroup 14, // 8: teleport.integration.v1.ListSubnetsResponse.subnets:type_name -> teleport.integration.v1.Subnet - 17, // 9: teleport.integration.v1.DeployDatabaseServiceRequest.deployments:type_name -> teleport.integration.v1.DeployDatabaseServiceDeployment - 22, // 10: teleport.integration.v1.EnrollEKSClustersResponse.results:type_name -> teleport.integration.v1.EnrollEKSClusterResult - 32, // 11: teleport.integration.v1.ListEC2Response.servers:type_name -> types.ServerV2 - 29, // 12: teleport.integration.v1.EKSCluster.labels:type_name -> teleport.integration.v1.EKSCluster.LabelsEntry - 30, // 13: teleport.integration.v1.EKSCluster.join_labels:type_name -> teleport.integration.v1.EKSCluster.JoinLabelsEntry - 27, // 14: teleport.integration.v1.ListEKSClustersResponse.clusters:type_name -> teleport.integration.v1.EKSCluster - 0, // 15: teleport.integration.v1.AWSOIDCService.ListEICE:input_type -> teleport.integration.v1.ListEICERequest - 3, // 16: teleport.integration.v1.AWSOIDCService.CreateEICE:input_type -> teleport.integration.v1.CreateEICERequest - 6, // 17: teleport.integration.v1.AWSOIDCService.ListDatabases:input_type -> teleport.integration.v1.ListDatabasesRequest - 8, // 18: teleport.integration.v1.AWSOIDCService.ListSecurityGroups:input_type -> teleport.integration.v1.ListSecurityGroupsRequest - 13, // 19: teleport.integration.v1.AWSOIDCService.ListSubnets:input_type -> teleport.integration.v1.ListSubnetsRequest - 16, // 20: teleport.integration.v1.AWSOIDCService.DeployDatabaseService:input_type -> teleport.integration.v1.DeployDatabaseServiceRequest - 19, // 21: teleport.integration.v1.AWSOIDCService.DeployService:input_type -> teleport.integration.v1.DeployServiceRequest - 21, // 22: teleport.integration.v1.AWSOIDCService.EnrollEKSClusters:input_type -> teleport.integration.v1.EnrollEKSClustersRequest - 24, // 23: teleport.integration.v1.AWSOIDCService.ListEC2:input_type -> teleport.integration.v1.ListEC2Request - 26, // 24: teleport.integration.v1.AWSOIDCService.ListEKSClusters:input_type -> teleport.integration.v1.ListEKSClustersRequest - 2, // 25: teleport.integration.v1.AWSOIDCService.ListEICE:output_type -> teleport.integration.v1.ListEICEResponse - 5, // 26: teleport.integration.v1.AWSOIDCService.CreateEICE:output_type -> teleport.integration.v1.CreateEICEResponse - 7, // 27: teleport.integration.v1.AWSOIDCService.ListDatabases:output_type -> teleport.integration.v1.ListDatabasesResponse - 12, // 28: teleport.integration.v1.AWSOIDCService.ListSecurityGroups:output_type -> teleport.integration.v1.ListSecurityGroupsResponse - 15, // 29: teleport.integration.v1.AWSOIDCService.ListSubnets:output_type -> teleport.integration.v1.ListSubnetsResponse - 18, // 30: teleport.integration.v1.AWSOIDCService.DeployDatabaseService:output_type -> teleport.integration.v1.DeployDatabaseServiceResponse - 20, // 31: teleport.integration.v1.AWSOIDCService.DeployService:output_type -> teleport.integration.v1.DeployServiceResponse - 23, // 32: teleport.integration.v1.AWSOIDCService.EnrollEKSClusters:output_type -> teleport.integration.v1.EnrollEKSClustersResponse - 25, // 33: teleport.integration.v1.AWSOIDCService.ListEC2:output_type -> teleport.integration.v1.ListEC2Response - 28, // 34: teleport.integration.v1.AWSOIDCService.ListEKSClusters:output_type -> teleport.integration.v1.ListEKSClustersResponse - 25, // [25:35] is the sub-list for method output_type - 15, // [15:25] is the sub-list for method input_type - 15, // [15:15] is the sub-list for extension type_name - 15, // [15:15] is the sub-list for extension extendee - 0, // [0:15] is the sub-list for field type_name + 17, // 9: teleport.integration.v1.ListVPCsResponse.vpcs:type_name -> teleport.integration.v1.VPC + 20, // 10: teleport.integration.v1.DeployDatabaseServiceRequest.deployments:type_name -> teleport.integration.v1.DeployDatabaseServiceDeployment + 25, // 11: teleport.integration.v1.EnrollEKSClustersResponse.results:type_name -> teleport.integration.v1.EnrollEKSClusterResult + 35, // 12: teleport.integration.v1.ListEC2Response.servers:type_name -> types.ServerV2 + 32, // 13: teleport.integration.v1.EKSCluster.labels:type_name -> teleport.integration.v1.EKSCluster.LabelsEntry + 33, // 14: teleport.integration.v1.EKSCluster.join_labels:type_name -> teleport.integration.v1.EKSCluster.JoinLabelsEntry + 30, // 15: teleport.integration.v1.ListEKSClustersResponse.clusters:type_name -> teleport.integration.v1.EKSCluster + 0, // 16: teleport.integration.v1.AWSOIDCService.ListEICE:input_type -> teleport.integration.v1.ListEICERequest + 3, // 17: teleport.integration.v1.AWSOIDCService.CreateEICE:input_type -> teleport.integration.v1.CreateEICERequest + 6, // 18: teleport.integration.v1.AWSOIDCService.ListDatabases:input_type -> teleport.integration.v1.ListDatabasesRequest + 8, // 19: teleport.integration.v1.AWSOIDCService.ListSecurityGroups:input_type -> teleport.integration.v1.ListSecurityGroupsRequest + 13, // 20: teleport.integration.v1.AWSOIDCService.ListSubnets:input_type -> teleport.integration.v1.ListSubnetsRequest + 16, // 21: teleport.integration.v1.AWSOIDCService.ListVPCs:input_type -> teleport.integration.v1.ListVPCsRequest + 19, // 22: teleport.integration.v1.AWSOIDCService.DeployDatabaseService:input_type -> teleport.integration.v1.DeployDatabaseServiceRequest + 22, // 23: teleport.integration.v1.AWSOIDCService.DeployService:input_type -> teleport.integration.v1.DeployServiceRequest + 24, // 24: teleport.integration.v1.AWSOIDCService.EnrollEKSClusters:input_type -> teleport.integration.v1.EnrollEKSClustersRequest + 27, // 25: teleport.integration.v1.AWSOIDCService.ListEC2:input_type -> teleport.integration.v1.ListEC2Request + 29, // 26: teleport.integration.v1.AWSOIDCService.ListEKSClusters:input_type -> teleport.integration.v1.ListEKSClustersRequest + 2, // 27: teleport.integration.v1.AWSOIDCService.ListEICE:output_type -> teleport.integration.v1.ListEICEResponse + 5, // 28: teleport.integration.v1.AWSOIDCService.CreateEICE:output_type -> teleport.integration.v1.CreateEICEResponse + 7, // 29: teleport.integration.v1.AWSOIDCService.ListDatabases:output_type -> teleport.integration.v1.ListDatabasesResponse + 12, // 30: teleport.integration.v1.AWSOIDCService.ListSecurityGroups:output_type -> teleport.integration.v1.ListSecurityGroupsResponse + 15, // 31: teleport.integration.v1.AWSOIDCService.ListSubnets:output_type -> teleport.integration.v1.ListSubnetsResponse + 18, // 32: teleport.integration.v1.AWSOIDCService.ListVPCs:output_type -> teleport.integration.v1.ListVPCsResponse + 21, // 33: teleport.integration.v1.AWSOIDCService.DeployDatabaseService:output_type -> teleport.integration.v1.DeployDatabaseServiceResponse + 23, // 34: teleport.integration.v1.AWSOIDCService.DeployService:output_type -> teleport.integration.v1.DeployServiceResponse + 26, // 35: teleport.integration.v1.AWSOIDCService.EnrollEKSClusters:output_type -> teleport.integration.v1.EnrollEKSClustersResponse + 28, // 36: teleport.integration.v1.AWSOIDCService.ListEC2:output_type -> teleport.integration.v1.ListEC2Response + 31, // 37: teleport.integration.v1.AWSOIDCService.ListEKSClusters:output_type -> teleport.integration.v1.ListEKSClustersResponse + 27, // [27:38] is the sub-list for method output_type + 16, // [16:27] is the sub-list for method input_type + 16, // [16:16] is the sub-list for extension type_name + 16, // [16:16] is the sub-list for extension extendee + 0, // [0:16] is the sub-list for field type_name } func init() { file_teleport_integration_v1_awsoidc_service_proto_init() } @@ -2896,7 +3111,7 @@ func file_teleport_integration_v1_awsoidc_service_proto_init() { } } file_teleport_integration_v1_awsoidc_service_proto_msgTypes[16].Exporter = func(v any, i int) any { - switch v := v.(*DeployDatabaseServiceRequest); i { + switch v := v.(*ListVPCsRequest); i { case 0: return &v.state case 1: @@ -2908,7 +3123,7 @@ func file_teleport_integration_v1_awsoidc_service_proto_init() { } } file_teleport_integration_v1_awsoidc_service_proto_msgTypes[17].Exporter = func(v any, i int) any { - switch v := v.(*DeployDatabaseServiceDeployment); i { + switch v := v.(*VPC); i { case 0: return &v.state case 1: @@ -2920,7 +3135,7 @@ func file_teleport_integration_v1_awsoidc_service_proto_init() { } } file_teleport_integration_v1_awsoidc_service_proto_msgTypes[18].Exporter = func(v any, i int) any { - switch v := v.(*DeployDatabaseServiceResponse); i { + switch v := v.(*ListVPCsResponse); i { case 0: return &v.state case 1: @@ -2932,7 +3147,7 @@ func file_teleport_integration_v1_awsoidc_service_proto_init() { } } file_teleport_integration_v1_awsoidc_service_proto_msgTypes[19].Exporter = func(v any, i int) any { - switch v := v.(*DeployServiceRequest); i { + switch v := v.(*DeployDatabaseServiceRequest); i { case 0: return &v.state case 1: @@ -2944,7 +3159,7 @@ func file_teleport_integration_v1_awsoidc_service_proto_init() { } } file_teleport_integration_v1_awsoidc_service_proto_msgTypes[20].Exporter = func(v any, i int) any { - switch v := v.(*DeployServiceResponse); i { + switch v := v.(*DeployDatabaseServiceDeployment); i { case 0: return &v.state case 1: @@ -2956,7 +3171,7 @@ func file_teleport_integration_v1_awsoidc_service_proto_init() { } } file_teleport_integration_v1_awsoidc_service_proto_msgTypes[21].Exporter = func(v any, i int) any { - switch v := v.(*EnrollEKSClustersRequest); i { + switch v := v.(*DeployDatabaseServiceResponse); i { case 0: return &v.state case 1: @@ -2968,7 +3183,7 @@ func file_teleport_integration_v1_awsoidc_service_proto_init() { } } file_teleport_integration_v1_awsoidc_service_proto_msgTypes[22].Exporter = func(v any, i int) any { - switch v := v.(*EnrollEKSClusterResult); i { + switch v := v.(*DeployServiceRequest); i { case 0: return &v.state case 1: @@ -2980,7 +3195,7 @@ func file_teleport_integration_v1_awsoidc_service_proto_init() { } } file_teleport_integration_v1_awsoidc_service_proto_msgTypes[23].Exporter = func(v any, i int) any { - switch v := v.(*EnrollEKSClustersResponse); i { + switch v := v.(*DeployServiceResponse); i { case 0: return &v.state case 1: @@ -2992,7 +3207,7 @@ func file_teleport_integration_v1_awsoidc_service_proto_init() { } } file_teleport_integration_v1_awsoidc_service_proto_msgTypes[24].Exporter = func(v any, i int) any { - switch v := v.(*ListEC2Request); i { + switch v := v.(*EnrollEKSClustersRequest); i { case 0: return &v.state case 1: @@ -3004,7 +3219,7 @@ func file_teleport_integration_v1_awsoidc_service_proto_init() { } } file_teleport_integration_v1_awsoidc_service_proto_msgTypes[25].Exporter = func(v any, i int) any { - switch v := v.(*ListEC2Response); i { + switch v := v.(*EnrollEKSClusterResult); i { case 0: return &v.state case 1: @@ -3016,7 +3231,7 @@ func file_teleport_integration_v1_awsoidc_service_proto_init() { } } file_teleport_integration_v1_awsoidc_service_proto_msgTypes[26].Exporter = func(v any, i int) any { - switch v := v.(*ListEKSClustersRequest); i { + switch v := v.(*EnrollEKSClustersResponse); i { case 0: return &v.state case 1: @@ -3028,7 +3243,7 @@ func file_teleport_integration_v1_awsoidc_service_proto_init() { } } file_teleport_integration_v1_awsoidc_service_proto_msgTypes[27].Exporter = func(v any, i int) any { - switch v := v.(*EKSCluster); i { + switch v := v.(*ListEC2Request); i { case 0: return &v.state case 1: @@ -3040,6 +3255,42 @@ func file_teleport_integration_v1_awsoidc_service_proto_init() { } } file_teleport_integration_v1_awsoidc_service_proto_msgTypes[28].Exporter = func(v any, i int) any { + switch v := v.(*ListEC2Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_integration_v1_awsoidc_service_proto_msgTypes[29].Exporter = func(v any, i int) any { + switch v := v.(*ListEKSClustersRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_integration_v1_awsoidc_service_proto_msgTypes[30].Exporter = func(v any, i int) any { + switch v := v.(*EKSCluster); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_integration_v1_awsoidc_service_proto_msgTypes[31].Exporter = func(v any, i int) any { switch v := v.(*ListEKSClustersResponse); i { case 0: return &v.state @@ -3058,7 +3309,7 @@ func file_teleport_integration_v1_awsoidc_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_teleport_integration_v1_awsoidc_service_proto_rawDesc, NumEnums: 0, - NumMessages: 31, + NumMessages: 34, NumExtensions: 0, NumServices: 1, }, diff --git a/api/gen/proto/go/teleport/integration/v1/awsoidc_service_grpc.pb.go b/api/gen/proto/go/teleport/integration/v1/awsoidc_service_grpc.pb.go index 2e697affa78fe..06710804553d6 100644 --- a/api/gen/proto/go/teleport/integration/v1/awsoidc_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/integration/v1/awsoidc_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/integration/v1/awsoidc_service.proto @@ -29,8 +29,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( AWSOIDCService_ListEICE_FullMethodName = "/teleport.integration.v1.AWSOIDCService/ListEICE" @@ -38,6 +38,7 @@ const ( AWSOIDCService_ListDatabases_FullMethodName = "/teleport.integration.v1.AWSOIDCService/ListDatabases" AWSOIDCService_ListSecurityGroups_FullMethodName = "/teleport.integration.v1.AWSOIDCService/ListSecurityGroups" AWSOIDCService_ListSubnets_FullMethodName = "/teleport.integration.v1.AWSOIDCService/ListSubnets" + AWSOIDCService_ListVPCs_FullMethodName = "/teleport.integration.v1.AWSOIDCService/ListVPCs" AWSOIDCService_DeployDatabaseService_FullMethodName = "/teleport.integration.v1.AWSOIDCService/DeployDatabaseService" AWSOIDCService_DeployService_FullMethodName = "/teleport.integration.v1.AWSOIDCService/DeployService" AWSOIDCService_EnrollEKSClusters_FullMethodName = "/teleport.integration.v1.AWSOIDCService/EnrollEKSClusters" @@ -73,6 +74,10 @@ type AWSOIDCServiceClient interface { // It uses the following API: // https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSubnets.html ListSubnets(ctx context.Context, in *ListSubnetsRequest, opts ...grpc.CallOption) (*ListSubnetsResponse, error) + // ListVPCs returns a list of AWS VPCs. + // It uses the following API: + // https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html + ListVPCs(ctx context.Context, in *ListVPCsRequest, opts ...grpc.CallOption) (*ListVPCsResponse, error) // DeployDatabaseService deploys a Database Services to Amazon ECS. DeployDatabaseService(ctx context.Context, in *DeployDatabaseServiceRequest, opts ...grpc.CallOption) (*DeployDatabaseServiceResponse, error) // DeployService deploys an ECS Service to Amazon ECS. @@ -148,6 +153,16 @@ func (c *aWSOIDCServiceClient) ListSubnets(ctx context.Context, in *ListSubnetsR return out, nil } +func (c *aWSOIDCServiceClient) ListVPCs(ctx context.Context, in *ListVPCsRequest, opts ...grpc.CallOption) (*ListVPCsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListVPCsResponse) + err := c.cc.Invoke(ctx, AWSOIDCService_ListVPCs_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *aWSOIDCServiceClient) DeployDatabaseService(ctx context.Context, in *DeployDatabaseServiceRequest, opts ...grpc.CallOption) (*DeployDatabaseServiceResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DeployDatabaseServiceResponse) @@ -200,7 +215,7 @@ func (c *aWSOIDCServiceClient) ListEKSClusters(ctx context.Context, in *ListEKSC // AWSOIDCServiceServer is the server API for AWSOIDCService service. // All implementations must embed UnimplementedAWSOIDCServiceServer -// for forward compatibility +// for forward compatibility. // // AWSOIDCService provides access to AWS APIs using the AWS OIDC Integration. type AWSOIDCServiceServer interface { @@ -226,6 +241,10 @@ type AWSOIDCServiceServer interface { // It uses the following API: // https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSubnets.html ListSubnets(context.Context, *ListSubnetsRequest) (*ListSubnetsResponse, error) + // ListVPCs returns a list of AWS VPCs. + // It uses the following API: + // https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html + ListVPCs(context.Context, *ListVPCsRequest) (*ListVPCsResponse, error) // DeployDatabaseService deploys a Database Services to Amazon ECS. DeployDatabaseService(context.Context, *DeployDatabaseServiceRequest) (*DeployDatabaseServiceResponse, error) // DeployService deploys an ECS Service to Amazon ECS. @@ -244,9 +263,12 @@ type AWSOIDCServiceServer interface { mustEmbedUnimplementedAWSOIDCServiceServer() } -// UnimplementedAWSOIDCServiceServer must be embedded to have forward compatible implementations. -type UnimplementedAWSOIDCServiceServer struct { -} +// UnimplementedAWSOIDCServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedAWSOIDCServiceServer struct{} func (UnimplementedAWSOIDCServiceServer) ListEICE(context.Context, *ListEICERequest) (*ListEICEResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListEICE not implemented") @@ -263,6 +285,9 @@ func (UnimplementedAWSOIDCServiceServer) ListSecurityGroups(context.Context, *Li func (UnimplementedAWSOIDCServiceServer) ListSubnets(context.Context, *ListSubnetsRequest) (*ListSubnetsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListSubnets not implemented") } +func (UnimplementedAWSOIDCServiceServer) ListVPCs(context.Context, *ListVPCsRequest) (*ListVPCsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListVPCs not implemented") +} func (UnimplementedAWSOIDCServiceServer) DeployDatabaseService(context.Context, *DeployDatabaseServiceRequest) (*DeployDatabaseServiceResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method DeployDatabaseService not implemented") } @@ -279,6 +304,7 @@ func (UnimplementedAWSOIDCServiceServer) ListEKSClusters(context.Context, *ListE return nil, status.Errorf(codes.Unimplemented, "method ListEKSClusters not implemented") } func (UnimplementedAWSOIDCServiceServer) mustEmbedUnimplementedAWSOIDCServiceServer() {} +func (UnimplementedAWSOIDCServiceServer) testEmbeddedByValue() {} // UnsafeAWSOIDCServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to AWSOIDCServiceServer will @@ -288,6 +314,13 @@ type UnsafeAWSOIDCServiceServer interface { } func RegisterAWSOIDCServiceServer(s grpc.ServiceRegistrar, srv AWSOIDCServiceServer) { + // If the following call pancis, it indicates UnimplementedAWSOIDCServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&AWSOIDCService_ServiceDesc, srv) } @@ -381,6 +414,24 @@ func _AWSOIDCService_ListSubnets_Handler(srv interface{}, ctx context.Context, d return interceptor(ctx, in, info, handler) } +func _AWSOIDCService_ListVPCs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListVPCsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AWSOIDCServiceServer).ListVPCs(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AWSOIDCService_ListVPCs_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AWSOIDCServiceServer).ListVPCs(ctx, req.(*ListVPCsRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _AWSOIDCService_DeployDatabaseService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(DeployDatabaseServiceRequest) if err := dec(in); err != nil { @@ -498,6 +549,10 @@ var AWSOIDCService_ServiceDesc = grpc.ServiceDesc{ MethodName: "ListSubnets", Handler: _AWSOIDCService_ListSubnets_Handler, }, + { + MethodName: "ListVPCs", + Handler: _AWSOIDCService_ListVPCs_Handler, + }, { MethodName: "DeployDatabaseService", Handler: _AWSOIDCService_DeployDatabaseService_Handler, diff --git a/api/gen/proto/go/teleport/integration/v1/integration_service_grpc.pb.go b/api/gen/proto/go/teleport/integration/v1/integration_service_grpc.pb.go index dac1038e618ab..2a4bbdb3f9563 100644 --- a/api/gen/proto/go/teleport/integration/v1/integration_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/integration/v1/integration_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/integration/v1/integration_service.proto @@ -31,8 +31,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( IntegrationService_ListIntegrations_FullMethodName = "/teleport.integration.v1.IntegrationService/ListIntegrations" @@ -147,7 +147,7 @@ func (c *integrationServiceClient) GenerateAWSOIDCToken(ctx context.Context, in // IntegrationServiceServer is the server API for IntegrationService service. // All implementations must embed UnimplementedIntegrationServiceServer -// for forward compatibility +// for forward compatibility. // // IntegrationService provides methods to manage Integrations with 3rd party APIs. type IntegrationServiceServer interface { @@ -169,9 +169,12 @@ type IntegrationServiceServer interface { mustEmbedUnimplementedIntegrationServiceServer() } -// UnimplementedIntegrationServiceServer must be embedded to have forward compatible implementations. -type UnimplementedIntegrationServiceServer struct { -} +// UnimplementedIntegrationServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedIntegrationServiceServer struct{} func (UnimplementedIntegrationServiceServer) ListIntegrations(context.Context, *ListIntegrationsRequest) (*ListIntegrationsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListIntegrations not implemented") @@ -195,6 +198,7 @@ func (UnimplementedIntegrationServiceServer) GenerateAWSOIDCToken(context.Contex return nil, status.Errorf(codes.Unimplemented, "method GenerateAWSOIDCToken not implemented") } func (UnimplementedIntegrationServiceServer) mustEmbedUnimplementedIntegrationServiceServer() {} +func (UnimplementedIntegrationServiceServer) testEmbeddedByValue() {} // UnsafeIntegrationServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to IntegrationServiceServer will @@ -204,6 +208,13 @@ type UnsafeIntegrationServiceServer interface { } func RegisterIntegrationServiceServer(s grpc.ServiceRegistrar, srv IntegrationServiceServer) { + // If the following call pancis, it indicates UnimplementedIntegrationServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&IntegrationService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/kube/v1/kube_service_grpc.pb.go b/api/gen/proto/go/teleport/kube/v1/kube_service_grpc.pb.go index 2be5ac46ee7d6..95cd7d1cfcbb4 100644 --- a/api/gen/proto/go/teleport/kube/v1/kube_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/kube/v1/kube_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/kube/v1/kube_service.proto @@ -29,8 +29,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( KubeService_ListKubernetesResources_FullMethodName = "/teleport.kube.v1.KubeService/ListKubernetesResources" @@ -67,7 +67,7 @@ func (c *kubeServiceClient) ListKubernetesResources(ctx context.Context, in *Lis // KubeServiceServer is the server API for KubeService service. // All implementations must embed UnimplementedKubeServiceServer -// for forward compatibility +// for forward compatibility. // // KubeService provides methods to list Kubernetes resources when users are not allowed // to access the underlying cluster or resources but their `search_as_roles` allow. @@ -77,14 +77,18 @@ type KubeServiceServer interface { mustEmbedUnimplementedKubeServiceServer() } -// UnimplementedKubeServiceServer must be embedded to have forward compatible implementations. -type UnimplementedKubeServiceServer struct { -} +// UnimplementedKubeServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedKubeServiceServer struct{} func (UnimplementedKubeServiceServer) ListKubernetesResources(context.Context, *ListKubernetesResourcesRequest) (*ListKubernetesResourcesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListKubernetesResources not implemented") } func (UnimplementedKubeServiceServer) mustEmbedUnimplementedKubeServiceServer() {} +func (UnimplementedKubeServiceServer) testEmbeddedByValue() {} // UnsafeKubeServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to KubeServiceServer will @@ -94,6 +98,13 @@ type UnsafeKubeServiceServer interface { } func RegisterKubeServiceServer(s grpc.ServiceRegistrar, srv KubeServiceServer) { + // If the following call pancis, it indicates UnimplementedKubeServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&KubeService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/kubewaitingcontainer/v1/kubewaitingcontainer_service_grpc.pb.go b/api/gen/proto/go/teleport/kubewaitingcontainer/v1/kubewaitingcontainer_service_grpc.pb.go index 5d7ce460c5e50..bc81c2103f607 100644 --- a/api/gen/proto/go/teleport/kubewaitingcontainer/v1/kubewaitingcontainer_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/kubewaitingcontainer/v1/kubewaitingcontainer_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/kubewaitingcontainer/v1/kubewaitingcontainer_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( KubeWaitingContainersService_ListKubernetesWaitingContainers_FullMethodName = "/teleport.kubewaitingcontainer.v1.KubeWaitingContainersService/ListKubernetesWaitingContainers" @@ -112,7 +112,7 @@ func (c *kubeWaitingContainersServiceClient) DeleteKubernetesWaitingContainer(ct // KubeWaitingContainersServiceServer is the server API for KubeWaitingContainersService service. // All implementations must embed UnimplementedKubeWaitingContainersServiceServer -// for forward compatibility +// for forward compatibility. // // KubeWaitingContainersService manages Kubernetes ephemeral // containers that are waiting to be created until moderated @@ -133,9 +133,12 @@ type KubeWaitingContainersServiceServer interface { mustEmbedUnimplementedKubeWaitingContainersServiceServer() } -// UnimplementedKubeWaitingContainersServiceServer must be embedded to have forward compatible implementations. -type UnimplementedKubeWaitingContainersServiceServer struct { -} +// UnimplementedKubeWaitingContainersServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedKubeWaitingContainersServiceServer struct{} func (UnimplementedKubeWaitingContainersServiceServer) ListKubernetesWaitingContainers(context.Context, *ListKubernetesWaitingContainersRequest) (*ListKubernetesWaitingContainersResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListKubernetesWaitingContainers not implemented") @@ -151,6 +154,7 @@ func (UnimplementedKubeWaitingContainersServiceServer) DeleteKubernetesWaitingCo } func (UnimplementedKubeWaitingContainersServiceServer) mustEmbedUnimplementedKubeWaitingContainersServiceServer() { } +func (UnimplementedKubeWaitingContainersServiceServer) testEmbeddedByValue() {} // UnsafeKubeWaitingContainersServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to KubeWaitingContainersServiceServer will @@ -160,6 +164,13 @@ type UnsafeKubeWaitingContainersServiceServer interface { } func RegisterKubeWaitingContainersServiceServer(s grpc.ServiceRegistrar, srv KubeWaitingContainersServiceServer) { + // If the following call pancis, it indicates UnimplementedKubeWaitingContainersServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&KubeWaitingContainersService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/loginrule/v1/loginrule_service_grpc.pb.go b/api/gen/proto/go/teleport/loginrule/v1/loginrule_service_grpc.pb.go index fd138656fadf4..06dbd7c99cde0 100644 --- a/api/gen/proto/go/teleport/loginrule/v1/loginrule_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/loginrule/v1/loginrule_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/loginrule/v1/loginrule_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( LoginRuleService_CreateLoginRule_FullMethodName = "/teleport.loginrule.v1.LoginRuleService/CreateLoginRule" @@ -136,7 +136,7 @@ func (c *loginRuleServiceClient) TestLoginRule(ctx context.Context, in *TestLogi // LoginRuleServiceServer is the server API for LoginRuleService service. // All implementations must embed UnimplementedLoginRuleServiceServer -// for forward compatibility +// for forward compatibility. // // LoginRuleService provides CRUD methods for the LoginRule resource. type LoginRuleServiceServer interface { @@ -159,9 +159,12 @@ type LoginRuleServiceServer interface { mustEmbedUnimplementedLoginRuleServiceServer() } -// UnimplementedLoginRuleServiceServer must be embedded to have forward compatible implementations. -type UnimplementedLoginRuleServiceServer struct { -} +// UnimplementedLoginRuleServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedLoginRuleServiceServer struct{} func (UnimplementedLoginRuleServiceServer) CreateLoginRule(context.Context, *CreateLoginRuleRequest) (*LoginRule, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateLoginRule not implemented") @@ -182,6 +185,7 @@ func (UnimplementedLoginRuleServiceServer) TestLoginRule(context.Context, *TestL return nil, status.Errorf(codes.Unimplemented, "method TestLoginRule not implemented") } func (UnimplementedLoginRuleServiceServer) mustEmbedUnimplementedLoginRuleServiceServer() {} +func (UnimplementedLoginRuleServiceServer) testEmbeddedByValue() {} // UnsafeLoginRuleServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to LoginRuleServiceServer will @@ -191,6 +195,13 @@ type UnsafeLoginRuleServiceServer interface { } func RegisterLoginRuleServiceServer(s grpc.ServiceRegistrar, srv LoginRuleServiceServer) { + // If the following call pancis, it indicates UnimplementedLoginRuleServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&LoginRuleService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/machineid/v1/bot_instance_service_grpc.pb.go b/api/gen/proto/go/teleport/machineid/v1/bot_instance_service_grpc.pb.go index 94d2617a7231e..520fa3209acae 100644 --- a/api/gen/proto/go/teleport/machineid/v1/bot_instance_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/machineid/v1/bot_instance_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/machineid/v1/bot_instance_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( BotInstanceService_GetBotInstance_FullMethodName = "/teleport.machineid.v1.BotInstanceService/GetBotInstance" @@ -106,7 +106,7 @@ func (c *botInstanceServiceClient) SubmitHeartbeat(ctx context.Context, in *Subm // BotInstanceServiceServer is the server API for BotInstanceService service. // All implementations must embed UnimplementedBotInstanceServiceServer -// for forward compatibility +// for forward compatibility. // // BotInstanceService provides functions to record and manage bot instances. type BotInstanceServiceServer interface { @@ -121,9 +121,12 @@ type BotInstanceServiceServer interface { mustEmbedUnimplementedBotInstanceServiceServer() } -// UnimplementedBotInstanceServiceServer must be embedded to have forward compatible implementations. -type UnimplementedBotInstanceServiceServer struct { -} +// UnimplementedBotInstanceServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedBotInstanceServiceServer struct{} func (UnimplementedBotInstanceServiceServer) GetBotInstance(context.Context, *GetBotInstanceRequest) (*BotInstance, error) { return nil, status.Errorf(codes.Unimplemented, "method GetBotInstance not implemented") @@ -138,6 +141,7 @@ func (UnimplementedBotInstanceServiceServer) SubmitHeartbeat(context.Context, *S return nil, status.Errorf(codes.Unimplemented, "method SubmitHeartbeat not implemented") } func (UnimplementedBotInstanceServiceServer) mustEmbedUnimplementedBotInstanceServiceServer() {} +func (UnimplementedBotInstanceServiceServer) testEmbeddedByValue() {} // UnsafeBotInstanceServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to BotInstanceServiceServer will @@ -147,6 +151,13 @@ type UnsafeBotInstanceServiceServer interface { } func RegisterBotInstanceServiceServer(s grpc.ServiceRegistrar, srv BotInstanceServiceServer) { + // If the following call pancis, it indicates UnimplementedBotInstanceServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&BotInstanceService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/machineid/v1/bot_service_grpc.pb.go b/api/gen/proto/go/teleport/machineid/v1/bot_service_grpc.pb.go index cad254d963677..2223fcc4bd3aa 100644 --- a/api/gen/proto/go/teleport/machineid/v1/bot_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/machineid/v1/bot_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/machineid/v1/bot_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( BotService_GetBot_FullMethodName = "/teleport.machineid.v1.BotService/GetBot" @@ -143,7 +143,7 @@ func (c *botServiceClient) DeleteBot(ctx context.Context, in *DeleteBotRequest, // BotServiceServer is the server API for BotService service. // All implementations must embed UnimplementedBotServiceServer -// for forward compatibility +// for forward compatibility. // // BotService provides methods to manage Teleport Bots type BotServiceServer interface { @@ -173,9 +173,12 @@ type BotServiceServer interface { mustEmbedUnimplementedBotServiceServer() } -// UnimplementedBotServiceServer must be embedded to have forward compatible implementations. -type UnimplementedBotServiceServer struct { -} +// UnimplementedBotServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedBotServiceServer struct{} func (UnimplementedBotServiceServer) GetBot(context.Context, *GetBotRequest) (*Bot, error) { return nil, status.Errorf(codes.Unimplemented, "method GetBot not implemented") @@ -196,6 +199,7 @@ func (UnimplementedBotServiceServer) DeleteBot(context.Context, *DeleteBotReques return nil, status.Errorf(codes.Unimplemented, "method DeleteBot not implemented") } func (UnimplementedBotServiceServer) mustEmbedUnimplementedBotServiceServer() {} +func (UnimplementedBotServiceServer) testEmbeddedByValue() {} // UnsafeBotServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to BotServiceServer will @@ -205,6 +209,13 @@ type UnsafeBotServiceServer interface { } func RegisterBotServiceServer(s grpc.ServiceRegistrar, srv BotServiceServer) { + // If the following call pancis, it indicates UnimplementedBotServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&BotService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/machineid/v1/workload_identity_service_grpc.pb.go b/api/gen/proto/go/teleport/machineid/v1/workload_identity_service_grpc.pb.go index 5d600458af5c8..aba1cac59fba5 100644 --- a/api/gen/proto/go/teleport/machineid/v1/workload_identity_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/machineid/v1/workload_identity_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/machineid/v1/workload_identity_service.proto @@ -29,8 +29,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( WorkloadIdentityService_SignX509SVIDs_FullMethodName = "/teleport.machineid.v1.WorkloadIdentityService/SignX509SVIDs" @@ -68,7 +68,7 @@ func (c *workloadIdentityServiceClient) SignX509SVIDs(ctx context.Context, in *S // WorkloadIdentityServiceServer is the server API for WorkloadIdentityService service. // All implementations must embed UnimplementedWorkloadIdentityServiceServer -// for forward compatibility +// for forward compatibility. // // WorkloadIdentityService provides the signing of workload identity documents. // It currently only supports signing SPIFFE x509 SVIDs. @@ -79,15 +79,19 @@ type WorkloadIdentityServiceServer interface { mustEmbedUnimplementedWorkloadIdentityServiceServer() } -// UnimplementedWorkloadIdentityServiceServer must be embedded to have forward compatible implementations. -type UnimplementedWorkloadIdentityServiceServer struct { -} +// UnimplementedWorkloadIdentityServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedWorkloadIdentityServiceServer struct{} func (UnimplementedWorkloadIdentityServiceServer) SignX509SVIDs(context.Context, *SignX509SVIDsRequest) (*SignX509SVIDsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SignX509SVIDs not implemented") } func (UnimplementedWorkloadIdentityServiceServer) mustEmbedUnimplementedWorkloadIdentityServiceServer() { } +func (UnimplementedWorkloadIdentityServiceServer) testEmbeddedByValue() {} // UnsafeWorkloadIdentityServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to WorkloadIdentityServiceServer will @@ -97,6 +101,13 @@ type UnsafeWorkloadIdentityServiceServer interface { } func RegisterWorkloadIdentityServiceServer(s grpc.ServiceRegistrar, srv WorkloadIdentityServiceServer) { + // If the following call pancis, it indicates UnimplementedWorkloadIdentityServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&WorkloadIdentityService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/notifications/v1/notifications_service_grpc.pb.go b/api/gen/proto/go/teleport/notifications/v1/notifications_service_grpc.pb.go index 1774ed66652fe..0a24d73cc69aa 100644 --- a/api/gen/proto/go/teleport/notifications/v1/notifications_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/notifications/v1/notifications_service_grpc.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/notifications/v1/notifications_service.proto @@ -33,8 +33,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( NotificationService_CreateUserNotification_FullMethodName = "/teleport.notifications.v1.NotificationService/CreateUserNotification" @@ -148,7 +148,7 @@ func (c *notificationServiceClient) UpsertUserLastSeenNotification(ctx context.C // NotificationServiceServer is the server API for NotificationService service. // All implementations must embed UnimplementedNotificationServiceServer -// for forward compatibility +// for forward compatibility. // // NotificationService provides CRUD operations for notifications resources. type NotificationServiceServer interface { @@ -169,9 +169,12 @@ type NotificationServiceServer interface { mustEmbedUnimplementedNotificationServiceServer() } -// UnimplementedNotificationServiceServer must be embedded to have forward compatible implementations. -type UnimplementedNotificationServiceServer struct { -} +// UnimplementedNotificationServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedNotificationServiceServer struct{} func (UnimplementedNotificationServiceServer) CreateUserNotification(context.Context, *CreateUserNotificationRequest) (*Notification, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateUserNotification not implemented") @@ -195,6 +198,7 @@ func (UnimplementedNotificationServiceServer) UpsertUserLastSeenNotification(con return nil, status.Errorf(codes.Unimplemented, "method UpsertUserLastSeenNotification not implemented") } func (UnimplementedNotificationServiceServer) mustEmbedUnimplementedNotificationServiceServer() {} +func (UnimplementedNotificationServiceServer) testEmbeddedByValue() {} // UnsafeNotificationServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to NotificationServiceServer will @@ -204,6 +208,13 @@ type UnsafeNotificationServiceServer interface { } func RegisterNotificationServiceServer(s grpc.ServiceRegistrar, srv NotificationServiceServer) { + // If the following call pancis, it indicates UnimplementedNotificationServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&NotificationService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/okta/v1/okta_service_grpc.pb.go b/api/gen/proto/go/teleport/okta/v1/okta_service_grpc.pb.go index 4a3ea01a1f53e..05ca5841e8274 100644 --- a/api/gen/proto/go/teleport/okta/v1/okta_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/okta/v1/okta_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/okta/v1/okta_service.proto @@ -31,8 +31,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( OktaService_ListOktaImportRules_FullMethodName = "/teleport.okta.v1.OktaService/ListOktaImportRules" @@ -224,7 +224,7 @@ func (c *oktaServiceClient) DeleteAllOktaAssignments(ctx context.Context, in *De // OktaServiceServer is the server API for OktaService service. // All implementations must embed UnimplementedOktaServiceServer -// for forward compatibility +// for forward compatibility. // // OktaService provides CRUD methods for Okta resources. type OktaServiceServer interface { @@ -257,9 +257,12 @@ type OktaServiceServer interface { mustEmbedUnimplementedOktaServiceServer() } -// UnimplementedOktaServiceServer must be embedded to have forward compatible implementations. -type UnimplementedOktaServiceServer struct { -} +// UnimplementedOktaServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedOktaServiceServer struct{} func (UnimplementedOktaServiceServer) ListOktaImportRules(context.Context, *ListOktaImportRulesRequest) (*ListOktaImportRulesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListOktaImportRules not implemented") @@ -301,6 +304,7 @@ func (UnimplementedOktaServiceServer) DeleteAllOktaAssignments(context.Context, return nil, status.Errorf(codes.Unimplemented, "method DeleteAllOktaAssignments not implemented") } func (UnimplementedOktaServiceServer) mustEmbedUnimplementedOktaServiceServer() {} +func (UnimplementedOktaServiceServer) testEmbeddedByValue() {} // UnsafeOktaServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to OktaServiceServer will @@ -310,6 +314,13 @@ type UnsafeOktaServiceServer interface { } func RegisterOktaServiceServer(s grpc.ServiceRegistrar, srv OktaServiceServer) { + // If the following call pancis, it indicates UnimplementedOktaServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&OktaService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/plugins/v1/plugin_service_grpc.pb.go b/api/gen/proto/go/teleport/plugins/v1/plugin_service_grpc.pb.go index 1ea0bb21d8d4c..aab519692c473 100644 --- a/api/gen/proto/go/teleport/plugins/v1/plugin_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/plugins/v1/plugin_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/plugins/v1/plugin_service.proto @@ -31,8 +31,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( PluginService_CreatePlugin_FullMethodName = "/teleport.plugins.v1.PluginService/CreatePlugin" @@ -202,7 +202,7 @@ func (c *pluginServiceClient) Cleanup(ctx context.Context, in *CleanupRequest, o // PluginServiceServer is the server API for PluginService service. // All implementations must embed UnimplementedPluginServiceServer -// for forward compatibility +// for forward compatibility. // // PluginService provides CRUD operations for Plugin resources. type PluginServiceServer interface { @@ -235,9 +235,12 @@ type PluginServiceServer interface { mustEmbedUnimplementedPluginServiceServer() } -// UnimplementedPluginServiceServer must be embedded to have forward compatible implementations. -type UnimplementedPluginServiceServer struct { -} +// UnimplementedPluginServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedPluginServiceServer struct{} func (UnimplementedPluginServiceServer) CreatePlugin(context.Context, *CreatePluginRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method CreatePlugin not implemented") @@ -273,6 +276,7 @@ func (UnimplementedPluginServiceServer) Cleanup(context.Context, *CleanupRequest return nil, status.Errorf(codes.Unimplemented, "method Cleanup not implemented") } func (UnimplementedPluginServiceServer) mustEmbedUnimplementedPluginServiceServer() {} +func (UnimplementedPluginServiceServer) testEmbeddedByValue() {} // UnsafePluginServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to PluginServiceServer will @@ -282,6 +286,13 @@ type UnsafePluginServiceServer interface { } func RegisterPluginServiceServer(s grpc.ServiceRegistrar, srv PluginServiceServer) { + // If the following call pancis, it indicates UnimplementedPluginServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&PluginService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/presence/v1/service_grpc.pb.go b/api/gen/proto/go/teleport/presence/v1/service_grpc.pb.go index 973054bb808c7..85f0ff52f8a96 100644 --- a/api/gen/proto/go/teleport/presence/v1/service_grpc.pb.go +++ b/api/gen/proto/go/teleport/presence/v1/service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/presence/v1/service.proto @@ -31,8 +31,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( PresenceService_GetRemoteCluster_FullMethodName = "/teleport.presence.v1.PresenceService/GetRemoteCluster" @@ -107,7 +107,7 @@ func (c *presenceServiceClient) DeleteRemoteCluster(ctx context.Context, in *Del // PresenceServiceServer is the server API for PresenceService service. // All implementations must embed UnimplementedPresenceServiceServer -// for forward compatibility +// for forward compatibility. // // PresenceService provides methods to manage presence of RemoteClusters type PresenceServiceServer interface { @@ -122,9 +122,12 @@ type PresenceServiceServer interface { mustEmbedUnimplementedPresenceServiceServer() } -// UnimplementedPresenceServiceServer must be embedded to have forward compatible implementations. -type UnimplementedPresenceServiceServer struct { -} +// UnimplementedPresenceServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedPresenceServiceServer struct{} func (UnimplementedPresenceServiceServer) GetRemoteCluster(context.Context, *GetRemoteClusterRequest) (*types.RemoteClusterV3, error) { return nil, status.Errorf(codes.Unimplemented, "method GetRemoteCluster not implemented") @@ -139,6 +142,7 @@ func (UnimplementedPresenceServiceServer) DeleteRemoteCluster(context.Context, * return nil, status.Errorf(codes.Unimplemented, "method DeleteRemoteCluster not implemented") } func (UnimplementedPresenceServiceServer) mustEmbedUnimplementedPresenceServiceServer() {} +func (UnimplementedPresenceServiceServer) testEmbeddedByValue() {} // UnsafePresenceServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to PresenceServiceServer will @@ -148,6 +152,13 @@ type UnsafePresenceServiceServer interface { } func RegisterPresenceServiceServer(s grpc.ServiceRegistrar, srv PresenceServiceServer) { + // If the following call pancis, it indicates UnimplementedPresenceServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&PresenceService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/resourceusage/v1/resourceusage_service_grpc.pb.go b/api/gen/proto/go/teleport/resourceusage/v1/resourceusage_service_grpc.pb.go index c82b4821e7e16..e610e182fbb77 100644 --- a/api/gen/proto/go/teleport/resourceusage/v1/resourceusage_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/resourceusage/v1/resourceusage_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/resourceusage/v1/resourceusage_service.proto @@ -29,8 +29,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( ResourceUsageService_GetUsage_FullMethodName = "/teleport.resourceusage.v1.ResourceUsageService/GetUsage" @@ -66,7 +66,7 @@ func (c *resourceUsageServiceClient) GetUsage(ctx context.Context, in *GetUsageR // ResourceUsageServiceServer is the server API for ResourceUsageService service. // All implementations must embed UnimplementedResourceUsageServiceServer -// for forward compatibility +// for forward compatibility. // // ResourceUsageService is a service to fetch information about the usage of limited resources on usage-billed plans. type ResourceUsageServiceServer interface { @@ -75,14 +75,18 @@ type ResourceUsageServiceServer interface { mustEmbedUnimplementedResourceUsageServiceServer() } -// UnimplementedResourceUsageServiceServer must be embedded to have forward compatible implementations. -type UnimplementedResourceUsageServiceServer struct { -} +// UnimplementedResourceUsageServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedResourceUsageServiceServer struct{} func (UnimplementedResourceUsageServiceServer) GetUsage(context.Context, *GetUsageRequest) (*GetUsageResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetUsage not implemented") } func (UnimplementedResourceUsageServiceServer) mustEmbedUnimplementedResourceUsageServiceServer() {} +func (UnimplementedResourceUsageServiceServer) testEmbeddedByValue() {} // UnsafeResourceUsageServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to ResourceUsageServiceServer will @@ -92,6 +96,13 @@ type UnsafeResourceUsageServiceServer interface { } func RegisterResourceUsageServiceServer(s grpc.ServiceRegistrar, srv ResourceUsageServiceServer) { + // If the following call pancis, it indicates UnimplementedResourceUsageServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&ResourceUsageService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/samlidp/v1/samlidp_grpc.pb.go b/api/gen/proto/go/teleport/samlidp/v1/samlidp_grpc.pb.go index 41f6c31477cac..366cfe60b1c36 100644 --- a/api/gen/proto/go/teleport/samlidp/v1/samlidp_grpc.pb.go +++ b/api/gen/proto/go/teleport/samlidp/v1/samlidp_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/samlidp/v1/samlidp.proto @@ -29,8 +29,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( SAMLIdPService_ProcessSAMLIdPRequest_FullMethodName = "/teleport.samlidp.v1.SAMLIdPService/ProcessSAMLIdPRequest" @@ -79,7 +79,7 @@ func (c *sAMLIdPServiceClient) TestSAMLIdPAttributeMapping(ctx context.Context, // SAMLIdPServiceServer is the server API for SAMLIdPService service. // All implementations must embed UnimplementedSAMLIdPServiceServer -// for forward compatibility +// for forward compatibility. // // SAMLIdPService provides utility methods for the SAML identity provider. type SAMLIdPServiceServer interface { @@ -90,9 +90,12 @@ type SAMLIdPServiceServer interface { mustEmbedUnimplementedSAMLIdPServiceServer() } -// UnimplementedSAMLIdPServiceServer must be embedded to have forward compatible implementations. -type UnimplementedSAMLIdPServiceServer struct { -} +// UnimplementedSAMLIdPServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedSAMLIdPServiceServer struct{} func (UnimplementedSAMLIdPServiceServer) ProcessSAMLIdPRequest(context.Context, *ProcessSAMLIdPRequestRequest) (*ProcessSAMLIdPRequestResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ProcessSAMLIdPRequest not implemented") @@ -101,6 +104,7 @@ func (UnimplementedSAMLIdPServiceServer) TestSAMLIdPAttributeMapping(context.Con return nil, status.Errorf(codes.Unimplemented, "method TestSAMLIdPAttributeMapping not implemented") } func (UnimplementedSAMLIdPServiceServer) mustEmbedUnimplementedSAMLIdPServiceServer() {} +func (UnimplementedSAMLIdPServiceServer) testEmbeddedByValue() {} // UnsafeSAMLIdPServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to SAMLIdPServiceServer will @@ -110,6 +114,13 @@ type UnsafeSAMLIdPServiceServer interface { } func RegisterSAMLIdPServiceServer(s grpc.ServiceRegistrar, srv SAMLIdPServiceServer) { + // If the following call pancis, it indicates UnimplementedSAMLIdPServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&SAMLIdPService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/scim/v1/scim_service_grpc.pb.go b/api/gen/proto/go/teleport/scim/v1/scim_service_grpc.pb.go index 3f0a50f31614f..a11499a4f02d6 100644 --- a/api/gen/proto/go/teleport/scim/v1/scim_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/scim/v1/scim_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/scim/v1/scim_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( SCIMService_ListSCIMResources_FullMethodName = "/teleport.scim.v1.SCIMService/ListSCIMResources" @@ -121,7 +121,7 @@ func (c *sCIMServiceClient) DeleteSCIMResource(ctx context.Context, in *DeleteSC // SCIMServiceServer is the server API for SCIMService service. // All implementations must embed UnimplementedSCIMServiceServer -// for forward compatibility +// for forward compatibility. // // SCIMService implements a SCIM gateway for external IDPs for user provisioning type SCIMServiceServer interface { @@ -140,9 +140,12 @@ type SCIMServiceServer interface { mustEmbedUnimplementedSCIMServiceServer() } -// UnimplementedSCIMServiceServer must be embedded to have forward compatible implementations. -type UnimplementedSCIMServiceServer struct { -} +// UnimplementedSCIMServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedSCIMServiceServer struct{} func (UnimplementedSCIMServiceServer) ListSCIMResources(context.Context, *ListSCIMResourcesRequest) (*ResourceList, error) { return nil, status.Errorf(codes.Unimplemented, "method ListSCIMResources not implemented") @@ -160,6 +163,7 @@ func (UnimplementedSCIMServiceServer) DeleteSCIMResource(context.Context, *Delet return nil, status.Errorf(codes.Unimplemented, "method DeleteSCIMResource not implemented") } func (UnimplementedSCIMServiceServer) mustEmbedUnimplementedSCIMServiceServer() {} +func (UnimplementedSCIMServiceServer) testEmbeddedByValue() {} // UnsafeSCIMServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to SCIMServiceServer will @@ -169,6 +173,13 @@ type UnsafeSCIMServiceServer interface { } func RegisterSCIMServiceServer(s grpc.ServiceRegistrar, srv SCIMServiceServer) { + // If the following call pancis, it indicates UnimplementedSCIMServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&SCIMService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/secreports/v1/secreports_service_grpc.pb.go b/api/gen/proto/go/teleport/secreports/v1/secreports_service_grpc.pb.go index bc25d091ee6f6..237c05958ca32 100644 --- a/api/gen/proto/go/teleport/secreports/v1/secreports_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/secreports/v1/secreports_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/secreports/v1/secreports_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( SecReportsService_UpsertAuditQuery_FullMethodName = "/teleport.secreports.v1.SecReportsService/UpsertAuditQuery" @@ -236,7 +236,7 @@ func (c *secReportsServiceClient) GetSchema(ctx context.Context, in *GetSchemaRe // SecReportsServiceServer is the server API for SecReportsService service. // All implementations must embed UnimplementedSecReportsServiceServer -// for forward compatibility +// for forward compatibility. // // SecReportsService is a service that manages security reports. type SecReportsServiceServer interface { @@ -271,9 +271,12 @@ type SecReportsServiceServer interface { mustEmbedUnimplementedSecReportsServiceServer() } -// UnimplementedSecReportsServiceServer must be embedded to have forward compatible implementations. -type UnimplementedSecReportsServiceServer struct { -} +// UnimplementedSecReportsServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedSecReportsServiceServer struct{} func (UnimplementedSecReportsServiceServer) UpsertAuditQuery(context.Context, *UpsertAuditQueryRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method UpsertAuditQuery not implemented") @@ -318,6 +321,7 @@ func (UnimplementedSecReportsServiceServer) GetSchema(context.Context, *GetSchem return nil, status.Errorf(codes.Unimplemented, "method GetSchema not implemented") } func (UnimplementedSecReportsServiceServer) mustEmbedUnimplementedSecReportsServiceServer() {} +func (UnimplementedSecReportsServiceServer) testEmbeddedByValue() {} // UnsafeSecReportsServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to SecReportsServiceServer will @@ -327,6 +331,13 @@ type UnsafeSecReportsServiceServer interface { } func RegisterSecReportsServiceServer(s grpc.ServiceRegistrar, srv SecReportsServiceServer) { + // If the following call pancis, it indicates UnimplementedSecReportsServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&SecReportsService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/transport/v1/transport_service_grpc.pb.go b/api/gen/proto/go/teleport/transport/v1/transport_service_grpc.pb.go index 5f4dae9862ac8..ac73fd815f5ff 100644 --- a/api/gen/proto/go/teleport/transport/v1/transport_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/transport/v1/transport_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/transport/v1/transport_service.proto @@ -29,8 +29,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( TransportService_GetClusterDetails_FullMethodName = "/teleport.transport.v1.TransportService/GetClusterDetails" @@ -57,13 +57,13 @@ type TransportServiceClient interface { // The client must first send a DialTarget before the connection is established. Agent frames // will be populated if SSH Agent forwarding is enabled for the connection. SSH frames contain // raw SSH payload to be processed by an x/crypto/ssh.Client or x/crypto/ssh.Server. - ProxySSH(ctx context.Context, opts ...grpc.CallOption) (TransportService_ProxySSHClient, error) + ProxySSH(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ProxySSHRequest, ProxySSHResponse], error) // ProxyCluster establishes a connection to the target cluster. // // The client must first send a ProxyClusterRequest with the desired cluster name before the // connection is established. After which the connection can be used to construct a new // auth.Client to the tunneled cluster. - ProxyCluster(ctx context.Context, opts ...grpc.CallOption) (TransportService_ProxyClusterClient, error) + ProxyCluster(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ProxyClusterRequest, ProxyClusterResponse], error) } type transportServiceClient struct { @@ -84,73 +84,35 @@ func (c *transportServiceClient) GetClusterDetails(ctx context.Context, in *GetC return out, nil } -func (c *transportServiceClient) ProxySSH(ctx context.Context, opts ...grpc.CallOption) (TransportService_ProxySSHClient, error) { +func (c *transportServiceClient) ProxySSH(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ProxySSHRequest, ProxySSHResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &TransportService_ServiceDesc.Streams[0], TransportService_ProxySSH_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &transportServiceProxySSHClient{ClientStream: stream} + x := &grpc.GenericClientStream[ProxySSHRequest, ProxySSHResponse]{ClientStream: stream} return x, nil } -type TransportService_ProxySSHClient interface { - Send(*ProxySSHRequest) error - Recv() (*ProxySSHResponse, error) - grpc.ClientStream -} - -type transportServiceProxySSHClient struct { - grpc.ClientStream -} - -func (x *transportServiceProxySSHClient) Send(m *ProxySSHRequest) error { - return x.ClientStream.SendMsg(m) -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type TransportService_ProxySSHClient = grpc.BidiStreamingClient[ProxySSHRequest, ProxySSHResponse] -func (x *transportServiceProxySSHClient) Recv() (*ProxySSHResponse, error) { - m := new(ProxySSHResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func (c *transportServiceClient) ProxyCluster(ctx context.Context, opts ...grpc.CallOption) (TransportService_ProxyClusterClient, error) { +func (c *transportServiceClient) ProxyCluster(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ProxyClusterRequest, ProxyClusterResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &TransportService_ServiceDesc.Streams[1], TransportService_ProxyCluster_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &transportServiceProxyClusterClient{ClientStream: stream} + x := &grpc.GenericClientStream[ProxyClusterRequest, ProxyClusterResponse]{ClientStream: stream} return x, nil } -type TransportService_ProxyClusterClient interface { - Send(*ProxyClusterRequest) error - Recv() (*ProxyClusterResponse, error) - grpc.ClientStream -} - -type transportServiceProxyClusterClient struct { - grpc.ClientStream -} - -func (x *transportServiceProxyClusterClient) Send(m *ProxyClusterRequest) error { - return x.ClientStream.SendMsg(m) -} - -func (x *transportServiceProxyClusterClient) Recv() (*ProxyClusterResponse, error) { - m := new(ProxyClusterResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type TransportService_ProxyClusterClient = grpc.BidiStreamingClient[ProxyClusterRequest, ProxyClusterResponse] // TransportServiceServer is the server API for TransportService service. // All implementations must embed UnimplementedTransportServiceServer -// for forward compatibility +// for forward compatibility. // // TransportService provides methods to proxy connections to various Teleport instances. // @@ -167,30 +129,34 @@ type TransportServiceServer interface { // The client must first send a DialTarget before the connection is established. Agent frames // will be populated if SSH Agent forwarding is enabled for the connection. SSH frames contain // raw SSH payload to be processed by an x/crypto/ssh.Client or x/crypto/ssh.Server. - ProxySSH(TransportService_ProxySSHServer) error + ProxySSH(grpc.BidiStreamingServer[ProxySSHRequest, ProxySSHResponse]) error // ProxyCluster establishes a connection to the target cluster. // // The client must first send a ProxyClusterRequest with the desired cluster name before the // connection is established. After which the connection can be used to construct a new // auth.Client to the tunneled cluster. - ProxyCluster(TransportService_ProxyClusterServer) error + ProxyCluster(grpc.BidiStreamingServer[ProxyClusterRequest, ProxyClusterResponse]) error mustEmbedUnimplementedTransportServiceServer() } -// UnimplementedTransportServiceServer must be embedded to have forward compatible implementations. -type UnimplementedTransportServiceServer struct { -} +// UnimplementedTransportServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedTransportServiceServer struct{} func (UnimplementedTransportServiceServer) GetClusterDetails(context.Context, *GetClusterDetailsRequest) (*GetClusterDetailsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetClusterDetails not implemented") } -func (UnimplementedTransportServiceServer) ProxySSH(TransportService_ProxySSHServer) error { +func (UnimplementedTransportServiceServer) ProxySSH(grpc.BidiStreamingServer[ProxySSHRequest, ProxySSHResponse]) error { return status.Errorf(codes.Unimplemented, "method ProxySSH not implemented") } -func (UnimplementedTransportServiceServer) ProxyCluster(TransportService_ProxyClusterServer) error { +func (UnimplementedTransportServiceServer) ProxyCluster(grpc.BidiStreamingServer[ProxyClusterRequest, ProxyClusterResponse]) error { return status.Errorf(codes.Unimplemented, "method ProxyCluster not implemented") } func (UnimplementedTransportServiceServer) mustEmbedUnimplementedTransportServiceServer() {} +func (UnimplementedTransportServiceServer) testEmbeddedByValue() {} // UnsafeTransportServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to TransportServiceServer will @@ -200,6 +166,13 @@ type UnsafeTransportServiceServer interface { } func RegisterTransportServiceServer(s grpc.ServiceRegistrar, srv TransportServiceServer) { + // If the following call pancis, it indicates UnimplementedTransportServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&TransportService_ServiceDesc, srv) } @@ -222,56 +195,18 @@ func _TransportService_GetClusterDetails_Handler(srv interface{}, ctx context.Co } func _TransportService_ProxySSH_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(TransportServiceServer).ProxySSH(&transportServiceProxySSHServer{ServerStream: stream}) -} - -type TransportService_ProxySSHServer interface { - Send(*ProxySSHResponse) error - Recv() (*ProxySSHRequest, error) - grpc.ServerStream -} - -type transportServiceProxySSHServer struct { - grpc.ServerStream + return srv.(TransportServiceServer).ProxySSH(&grpc.GenericServerStream[ProxySSHRequest, ProxySSHResponse]{ServerStream: stream}) } -func (x *transportServiceProxySSHServer) Send(m *ProxySSHResponse) error { - return x.ServerStream.SendMsg(m) -} - -func (x *transportServiceProxySSHServer) Recv() (*ProxySSHRequest, error) { - m := new(ProxySSHRequest) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type TransportService_ProxySSHServer = grpc.BidiStreamingServer[ProxySSHRequest, ProxySSHResponse] func _TransportService_ProxyCluster_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(TransportServiceServer).ProxyCluster(&transportServiceProxyClusterServer{ServerStream: stream}) + return srv.(TransportServiceServer).ProxyCluster(&grpc.GenericServerStream[ProxyClusterRequest, ProxyClusterResponse]{ServerStream: stream}) } -type TransportService_ProxyClusterServer interface { - Send(*ProxyClusterResponse) error - Recv() (*ProxyClusterRequest, error) - grpc.ServerStream -} - -type transportServiceProxyClusterServer struct { - grpc.ServerStream -} - -func (x *transportServiceProxyClusterServer) Send(m *ProxyClusterResponse) error { - return x.ServerStream.SendMsg(m) -} - -func (x *transportServiceProxyClusterServer) Recv() (*ProxyClusterRequest, error) { - m := new(ProxyClusterRequest) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type TransportService_ProxyClusterServer = grpc.BidiStreamingServer[ProxyClusterRequest, ProxyClusterResponse] // TransportService_ServiceDesc is the grpc.ServiceDesc for TransportService service. // It's only intended for direct use with grpc.RegisterService, diff --git a/api/gen/proto/go/teleport/trust/v1/trust_service_grpc.pb.go b/api/gen/proto/go/teleport/trust/v1/trust_service_grpc.pb.go index 3915be697442e..2b5e384451063 100644 --- a/api/gen/proto/go/teleport/trust/v1/trust_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/trust/v1/trust_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/trust/v1/trust_service.proto @@ -31,8 +31,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( TrustService_GetCertAuthority_FullMethodName = "/teleport.trust.v1.TrustService/GetCertAuthority" @@ -147,7 +147,7 @@ func (c *trustServiceClient) GenerateHostCert(ctx context.Context, in *GenerateH // TrustServiceServer is the server API for TrustService service. // All implementations must embed UnimplementedTrustServiceServer -// for forward compatibility +// for forward compatibility. // // TrustService provides methods to manage certificate authorities. type TrustServiceServer interface { @@ -169,9 +169,12 @@ type TrustServiceServer interface { mustEmbedUnimplementedTrustServiceServer() } -// UnimplementedTrustServiceServer must be embedded to have forward compatible implementations. -type UnimplementedTrustServiceServer struct { -} +// UnimplementedTrustServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedTrustServiceServer struct{} func (UnimplementedTrustServiceServer) GetCertAuthority(context.Context, *GetCertAuthorityRequest) (*types.CertAuthorityV2, error) { return nil, status.Errorf(codes.Unimplemented, "method GetCertAuthority not implemented") @@ -195,6 +198,7 @@ func (UnimplementedTrustServiceServer) GenerateHostCert(context.Context, *Genera return nil, status.Errorf(codes.Unimplemented, "method GenerateHostCert not implemented") } func (UnimplementedTrustServiceServer) mustEmbedUnimplementedTrustServiceServer() {} +func (UnimplementedTrustServiceServer) testEmbeddedByValue() {} // UnsafeTrustServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to TrustServiceServer will @@ -204,6 +208,13 @@ type UnsafeTrustServiceServer interface { } func RegisterTrustServiceServer(s grpc.ServiceRegistrar, srv TrustServiceServer) { + // If the following call pancis, it indicates UnimplementedTrustServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&TrustService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/userloginstate/v1/userloginstate_service_grpc.pb.go b/api/gen/proto/go/teleport/userloginstate/v1/userloginstate_service_grpc.pb.go index 521a09fdfc699..1f085cb26b756 100644 --- a/api/gen/proto/go/teleport/userloginstate/v1/userloginstate_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/userloginstate/v1/userloginstate_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/userloginstate/v1/userloginstate_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( UserLoginStateService_GetUserLoginStates_FullMethodName = "/teleport.userloginstate.v1.UserLoginStateService/GetUserLoginStates" @@ -119,7 +119,7 @@ func (c *userLoginStateServiceClient) DeleteAllUserLoginStates(ctx context.Conte // UserLoginStateServiceServer is the server API for UserLoginStateService service. // All implementations must embed UnimplementedUserLoginStateServiceServer -// for forward compatibility +// for forward compatibility. // // UserLoginStateService provides CRUD methods for user login state resources. type UserLoginStateServiceServer interface { @@ -136,9 +136,12 @@ type UserLoginStateServiceServer interface { mustEmbedUnimplementedUserLoginStateServiceServer() } -// UnimplementedUserLoginStateServiceServer must be embedded to have forward compatible implementations. -type UnimplementedUserLoginStateServiceServer struct { -} +// UnimplementedUserLoginStateServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedUserLoginStateServiceServer struct{} func (UnimplementedUserLoginStateServiceServer) GetUserLoginStates(context.Context, *GetUserLoginStatesRequest) (*GetUserLoginStatesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetUserLoginStates not implemented") @@ -156,6 +159,7 @@ func (UnimplementedUserLoginStateServiceServer) DeleteAllUserLoginStates(context return nil, status.Errorf(codes.Unimplemented, "method DeleteAllUserLoginStates not implemented") } func (UnimplementedUserLoginStateServiceServer) mustEmbedUnimplementedUserLoginStateServiceServer() {} +func (UnimplementedUserLoginStateServiceServer) testEmbeddedByValue() {} // UnsafeUserLoginStateServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to UserLoginStateServiceServer will @@ -165,6 +169,13 @@ type UnsafeUserLoginStateServiceServer interface { } func RegisterUserLoginStateServiceServer(s grpc.ServiceRegistrar, srv UserLoginStateServiceServer) { + // If the following call pancis, it indicates UnimplementedUserLoginStateServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&UserLoginStateService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/users/v1/users_service_grpc.pb.go b/api/gen/proto/go/teleport/users/v1/users_service_grpc.pb.go index 271665bfecaa4..8fcbd9d2e7b64 100644 --- a/api/gen/proto/go/teleport/users/v1/users_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/users/v1/users_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/users/v1/users_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( UsersService_GetUser_FullMethodName = "/teleport.users.v1.UsersService/GetUser" @@ -132,7 +132,7 @@ func (c *usersServiceClient) DeleteUser(ctx context.Context, in *DeleteUserReque // UsersServiceServer is the server API for UsersService service. // All implementations must embed UnimplementedUsersServiceServer -// for forward compatibility +// for forward compatibility. // // UsersService provides methods to manage Teleport users. type UsersServiceServer interface { @@ -151,9 +151,12 @@ type UsersServiceServer interface { mustEmbedUnimplementedUsersServiceServer() } -// UnimplementedUsersServiceServer must be embedded to have forward compatible implementations. -type UnimplementedUsersServiceServer struct { -} +// UnimplementedUsersServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedUsersServiceServer struct{} func (UnimplementedUsersServiceServer) GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetUser not implemented") @@ -174,6 +177,7 @@ func (UnimplementedUsersServiceServer) DeleteUser(context.Context, *DeleteUserRe return nil, status.Errorf(codes.Unimplemented, "method DeleteUser not implemented") } func (UnimplementedUsersServiceServer) mustEmbedUnimplementedUsersServiceServer() {} +func (UnimplementedUsersServiceServer) testEmbeddedByValue() {} // UnsafeUsersServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to UsersServiceServer will @@ -183,6 +187,13 @@ type UnsafeUsersServiceServer interface { } func RegisterUsersServiceServer(s grpc.ServiceRegistrar, srv UsersServiceServer) { + // If the following call pancis, it indicates UnimplementedUsersServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&UsersService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/teleport/vnet/v1/vnet_config_service_grpc.pb.go b/api/gen/proto/go/teleport/vnet/v1/vnet_config_service_grpc.pb.go index e17d0a3d1d21f..778c3e4252322 100644 --- a/api/gen/proto/go/teleport/vnet/v1/vnet_config_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/vnet/v1/vnet_config_service_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/vnet/v1/vnet_config_service.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( VnetConfigService_GetVnetConfig_FullMethodName = "/teleport.vnet.v1.VnetConfigService/GetVnetConfig" @@ -119,7 +119,7 @@ func (c *vnetConfigServiceClient) DeleteVnetConfig(ctx context.Context, in *Dele // VnetConfigServiceServer is the server API for VnetConfigService service. // All implementations must embed UnimplementedVnetConfigServiceServer -// for forward compatibility +// for forward compatibility. // // VnetConfigService provides an API to manage the singleton VnetConfig. type VnetConfigServiceServer interface { @@ -136,9 +136,12 @@ type VnetConfigServiceServer interface { mustEmbedUnimplementedVnetConfigServiceServer() } -// UnimplementedVnetConfigServiceServer must be embedded to have forward compatible implementations. -type UnimplementedVnetConfigServiceServer struct { -} +// UnimplementedVnetConfigServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedVnetConfigServiceServer struct{} func (UnimplementedVnetConfigServiceServer) GetVnetConfig(context.Context, *GetVnetConfigRequest) (*VnetConfig, error) { return nil, status.Errorf(codes.Unimplemented, "method GetVnetConfig not implemented") @@ -156,6 +159,7 @@ func (UnimplementedVnetConfigServiceServer) DeleteVnetConfig(context.Context, *D return nil, status.Errorf(codes.Unimplemented, "method DeleteVnetConfig not implemented") } func (UnimplementedVnetConfigServiceServer) mustEmbedUnimplementedVnetConfigServiceServer() {} +func (UnimplementedVnetConfigServiceServer) testEmbeddedByValue() {} // UnsafeVnetConfigServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to VnetConfigServiceServer will @@ -165,6 +169,13 @@ type UnsafeVnetConfigServiceServer interface { } func RegisterVnetConfigServiceServer(s grpc.ServiceRegistrar, srv VnetConfigServiceServer) { + // If the following call pancis, it indicates UnimplementedVnetConfigServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&VnetConfigService_ServiceDesc, srv) } diff --git a/api/gen/proto/go/userpreferences/v1/userpreferences_grpc.pb.go b/api/gen/proto/go/userpreferences/v1/userpreferences_grpc.pb.go index c7163c0936509..f4dc0f68258fb 100644 --- a/api/gen/proto/go/userpreferences/v1/userpreferences_grpc.pb.go +++ b/api/gen/proto/go/userpreferences/v1/userpreferences_grpc.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/userpreferences/v1/userpreferences.proto @@ -30,8 +30,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( UserPreferencesService_GetUserPreferences_FullMethodName = "/teleport.userpreferences.v1.UserPreferencesService/GetUserPreferences" @@ -80,7 +80,7 @@ func (c *userPreferencesServiceClient) UpsertUserPreferences(ctx context.Context // UserPreferencesServiceServer is the server API for UserPreferencesService service. // All implementations must embed UnimplementedUserPreferencesServiceServer -// for forward compatibility +// for forward compatibility. // // UserPreferencesService is a service that stores user settings. type UserPreferencesServiceServer interface { @@ -91,9 +91,12 @@ type UserPreferencesServiceServer interface { mustEmbedUnimplementedUserPreferencesServiceServer() } -// UnimplementedUserPreferencesServiceServer must be embedded to have forward compatible implementations. -type UnimplementedUserPreferencesServiceServer struct { -} +// UnimplementedUserPreferencesServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedUserPreferencesServiceServer struct{} func (UnimplementedUserPreferencesServiceServer) GetUserPreferences(context.Context, *GetUserPreferencesRequest) (*GetUserPreferencesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetUserPreferences not implemented") @@ -103,6 +106,7 @@ func (UnimplementedUserPreferencesServiceServer) UpsertUserPreferences(context.C } func (UnimplementedUserPreferencesServiceServer) mustEmbedUnimplementedUserPreferencesServiceServer() { } +func (UnimplementedUserPreferencesServiceServer) testEmbeddedByValue() {} // UnsafeUserPreferencesServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to UserPreferencesServiceServer will @@ -112,6 +116,13 @@ type UnsafeUserPreferencesServiceServer interface { } func RegisterUserPreferencesServiceServer(s grpc.ServiceRegistrar, srv UserPreferencesServiceServer) { + // If the following call pancis, it indicates UnimplementedUserPreferencesServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&UserPreferencesService_ServiceDesc, srv) } diff --git a/api/proto/teleport/integration/v1/awsoidc_service.proto b/api/proto/teleport/integration/v1/awsoidc_service.proto index adc4f17ed4953..0c0813df5ad7f 100644 --- a/api/proto/teleport/integration/v1/awsoidc_service.proto +++ b/api/proto/teleport/integration/v1/awsoidc_service.proto @@ -49,6 +49,11 @@ service AWSOIDCService { // https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSubnets.html rpc ListSubnets(ListSubnetsRequest) returns (ListSubnetsResponse); + // ListVPCs returns a list of AWS VPCs. + // It uses the following API: + // https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html + rpc ListVPCs(ListVPCsRequest) returns (ListVPCsResponse); + // DeployDatabaseService deploys a Database Services to Amazon ECS. rpc DeployDatabaseService(DeployDatabaseServiceRequest) returns (DeployDatabaseServiceResponse); @@ -262,7 +267,7 @@ message ListSubnetsRequest { // Subnet is a representation of an AWS VPC subnet. message Subnet { - // Name is the subnet name. + // Name is the subnet name. Can be empty. string name = 1; // ID is the subnet ID. string id = 2; @@ -279,6 +284,36 @@ message ListSubnetsResponse { string next_token = 2; } +// ListVPCsRequest is a request for a paginated list of AWS VPCs. +message ListVPCsRequest { + // Integration is the AWS OIDC Integration name. + // Required. + string integration = 1; + // Region is the AWS Region + // Required. + string region = 2; + // NextToken is the token to be used to fetch the next page. + // If empty, the first page is fetched. + string next_token = 3; +} + +// VPC is a representation of an AWS VPC. +message VPC { + // Name is the VPC name. Can be empty. + string name = 1; + // ID is the VPC ID. + string id = 2; +} + +// ListVPCsResponse contains a page of AWS VPCs. +message ListVPCsResponse { + // VPCs contains the page of VPCs. + repeated VPC vpcs = 1; + // NextToken is used for pagination. + // If non-empty, it can be used to request the next page. + string next_token = 2; +} + // DeployDatabaseServiceRequest is a request to deploy . message DeployDatabaseServiceRequest { // Integration is the AWS OIDC Integration name. diff --git a/api/proto/teleport/legacy/types/events/events.proto b/api/proto/teleport/legacy/types/events/events.proto index 5d4d5a6306933..a2eaa5bea9a11 100644 --- a/api/proto/teleport/legacy/types/events/events.proto +++ b/api/proto/teleport/legacy/types/events/events.proto @@ -111,6 +111,13 @@ message UserMetadata { // UserKind indicates what type of user this is, e.g. a human or Machine ID // bot user. UserKind UserKind = 10 [(gogoproto.jsontag) = "user_kind,omitempty"]; + + // BotName is the name of the Bot if this action is associated with one. + string BotName = 11 [(gogoproto.jsontag) = "bot_name,omitempty"]; + + // BotInstanceID is the ID of the Bot Instance if this action is associated + // with one. + string BotInstanceID = 12 [(gogoproto.jsontag) = "bot_instance_id,omitempty"]; } // Server is a server metadata @@ -3846,6 +3853,8 @@ message BotJoin { (gogoproto.embed) = true, (gogoproto.jsontag) = "" ]; + // BotInstanceID is the ID of the bot instance which has joined or renewed. + string BotInstanceID = 9 [(gogoproto.jsontag) = "bot_instance_id,omitempty"]; } // InstanceJoin records an instance join event. @@ -4575,6 +4584,9 @@ message Identity { // DeviceExtensions holds the device trust device extensions for the identity, // if any. DeviceExtensions DeviceExtensions = 28 [(gogoproto.jsontag) = "device_extensions,omitempty"]; + // BotInstanceID indicates the name of the Machine ID bot instance this + // identity was issued to, if any. + string BotInstanceID = 29 [(gogoproto.jsontag) = "bot_instance_id,omitempty"]; } // RouteToApp contains parameters for application access certificate requests. diff --git a/api/proto/teleport/legacy/types/types.proto b/api/proto/teleport/legacy/types/types.proto index 89c434cf15b05..5cb2446a40ac3 100644 --- a/api/proto/teleport/legacy/types/types.proto +++ b/api/proto/teleport/legacy/types/types.proto @@ -1254,7 +1254,7 @@ message ProvisionTokenSpecV2 { (gogoproto.casttype) = "Duration" ]; // JoinMethod is the joining method required in order to use this token. - // Supported joining methods include "token", "ec2", and "iam". + // Supported joining methods include: azure, circleci, ec2, gcp, github, gitlab, iam, kubernetes, spacelift, token, tpm string JoinMethod = 4 [ (gogoproto.jsontag) = "join_method", (gogoproto.casttype) = "JoinMethod" @@ -4506,6 +4506,10 @@ message OIDCAuthRequest { // ClientLoginIP specifies IP address of the client for login, it will be written to the user's certificates. string ClientLoginIP = 18 [(gogoproto.jsontag) = "client_login_ip,omitempty"]; + + // ClientUserAgent is the user agent of the Web browser, used for issuing a + // DeviceWebToken. + string ClientUserAgent = 19 [(gogoproto.jsontag) = "client_user_agent,omitempty"]; } // SAMLConnectorV2 represents a SAML connector. @@ -4648,6 +4652,10 @@ message SAMLAuthRequest { // ClientLoginIP specifies IP address of the client for login, it will be written to the user's certificates. string ClientLoginIP = 17 [(gogoproto.jsontag) = "client_login_ip,omitempty"]; + + // ClientUserAgent is the user agent of the Web browser, used for issuing a + // DeviceWebToken. + string ClientUserAgent = 18 [(gogoproto.jsontag) = "client_user_agent,omitempty"]; } // AttributeMapping maps a SAML attribute statement to teleport roles. diff --git a/api/types/accessgraph/private_key.go b/api/types/accessgraph/private_key.go index 57e0874ed040a..a8730685f8d3c 100644 --- a/api/types/accessgraph/private_key.go +++ b/api/types/accessgraph/private_key.go @@ -106,3 +106,18 @@ func hashComp(values ...string) string { } return hex.EncodeToString(h.Sum(nil)) } + +// DescribePublicKeyMode returns a human-readable description of the public key mode. +func DescribePublicKeyMode(mode accessgraphv1pb.PublicKeyMode) string { + switch mode { + case accessgraphv1pb.PublicKeyMode_PUBLIC_KEY_MODE_PUB_FILE: + return "used public key file" + case accessgraphv1pb.PublicKeyMode_PUBLIC_KEY_MODE_PROTECTED: + return "protected private key" + case accessgraphv1pb.PublicKeyMode_PUBLIC_KEY_MODE_DERIVED: + return "derived from private key" + default: + return "unknown" + } + +} diff --git a/api/types/events/events.pb.go b/api/types/events/events.pb.go index c27fdfa78a682..65ca9d3e48cce 100644 --- a/api/types/events/events.pb.go +++ b/api/types/events/events.pb.go @@ -500,7 +500,12 @@ type UserMetadata struct { RequiredPrivateKeyPolicy string `protobuf:"bytes,9,opt,name=RequiredPrivateKeyPolicy,proto3" json:"required_private_key_policy,omitempty"` // UserKind indicates what type of user this is, e.g. a human or Machine ID // bot user. - UserKind UserKind `protobuf:"varint,10,opt,name=UserKind,proto3,enum=events.UserKind" json:"user_kind,omitempty"` + UserKind UserKind `protobuf:"varint,10,opt,name=UserKind,proto3,enum=events.UserKind" json:"user_kind,omitempty"` + // BotName is the name of the Bot if this action is associated with one. + BotName string `protobuf:"bytes,11,opt,name=BotName,proto3" json:"bot_name,omitempty"` + // BotInstanceID is the ID of the Bot Instance if this action is associated + // with one. + BotInstanceID string `protobuf:"bytes,12,opt,name=BotInstanceID,proto3" json:"bot_instance_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -6554,7 +6559,9 @@ type BotJoin struct { // UserName is the name of the user associated with the bot which has joined. UserName string `protobuf:"bytes,7,opt,name=UserName,proto3" json:"user_name,omitempty"` // ConnectionMetadata holds information about the connection - ConnectionMetadata `protobuf:"bytes,8,opt,name=Connection,proto3,embedded=Connection" json:""` + ConnectionMetadata `protobuf:"bytes,8,opt,name=Connection,proto3,embedded=Connection" json:""` + // BotInstanceID is the ID of the bot instance which has joined or renewed. + BotInstanceID string `protobuf:"bytes,9,opt,name=BotInstanceID,proto3" json:"bot_instance_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -9727,10 +9734,13 @@ type Identity struct { BotName string `protobuf:"bytes,27,opt,name=BotName,proto3" json:"bot_name,omitempty"` // DeviceExtensions holds the device trust device extensions for the identity, // if any. - DeviceExtensions *DeviceExtensions `protobuf:"bytes,28,opt,name=DeviceExtensions,proto3" json:"device_extensions,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + DeviceExtensions *DeviceExtensions `protobuf:"bytes,28,opt,name=DeviceExtensions,proto3" json:"device_extensions,omitempty"` + // BotInstanceID indicates the name of the Machine ID bot instance this + // identity was issued to, if any. + BotInstanceID string `protobuf:"bytes,29,opt,name=BotInstanceID,proto3" json:"bot_instance_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Identity) Reset() { *m = Identity{} } @@ -13614,982 +13624,986 @@ func init() { } var fileDescriptor_007ba1c3d6266d56 = []byte{ - // 15593 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xbd, 0x6b, 0x74, 0x1c, 0xc9, - 0x75, 0x18, 0x8c, 0x79, 0x60, 0x00, 0x5c, 0x3c, 0x08, 0x14, 0x5f, 0x4d, 0x2e, 0xc9, 0xd9, 0xed, - 0xd5, 0x52, 0xe4, 0x6a, 0x97, 0xd4, 0x72, 0xb9, 0xbb, 0xda, 0x97, 0x76, 0x07, 0x18, 0x80, 0x98, - 0x25, 0x5e, 0xdb, 0x03, 0x92, 0x5a, 0xbd, 0xc6, 0x8d, 0xe9, 0x02, 0xd0, 0xcb, 0x99, 0xee, 0x51, - 0x77, 0x0f, 0x41, 0xec, 0xf7, 0xb2, 0xfc, 0xf9, 0x21, 0xf9, 0x93, 0x64, 0x7d, 0x72, 0x6c, 0xd9, - 0xb1, 0x13, 0xcb, 0xaf, 0xc4, 0xf1, 0x71, 0xec, 0x38, 0xc9, 0xb1, 0xad, 0x38, 0x3a, 0xb1, 0xa3, - 0x3c, 0xd6, 0xd1, 0x71, 0x8e, 0xed, 0x24, 0x3e, 0x3e, 0x89, 0x03, 0x39, 0x4a, 0x9c, 0x1f, 0x38, - 0xc9, 0x89, 0x93, 0xe8, 0xc4, 0x8e, 0xe3, 0xe4, 0xe4, 0xd4, 0xad, 0xea, 0xee, 0xea, 0xc7, 0x0c, - 0x9e, 0x6b, 0x2c, 0x44, 0xfc, 0x21, 0x31, 0xf7, 0xde, 0xba, 0x55, 0x7d, 0xeb, 0x56, 0xd5, 0xad, - 0xaa, 0x5b, 0xf7, 0xc2, 0x65, 0x8f, 0x36, 0x68, 0xcb, 0x76, 0xbc, 0xab, 0x0d, 0xba, 0xa2, 0xd7, - 0xd7, 0xaf, 0x7a, 0xeb, 0x2d, 0xea, 0x5e, 0xa5, 0xf7, 0xa8, 0xe5, 0xf9, 0xff, 0x5d, 0x69, 0x39, - 0xb6, 0x67, 0x93, 0x02, 0xff, 0x75, 0xf6, 0xc4, 0x8a, 0xbd, 0x62, 0x23, 0xe8, 0x2a, 0xfb, 0x8b, - 0x63, 0xcf, 0x9e, 0x5b, 0xb1, 0xed, 0x95, 0x06, 0xbd, 0x8a, 0xbf, 0x96, 0xda, 0xcb, 0x57, 0x5d, - 0xcf, 0x69, 0xd7, 0x3d, 0x81, 0x2d, 0xc6, 0xb1, 0x9e, 0xd9, 0xa4, 0xae, 0xa7, 0x37, 0x5b, 0x82, - 0xe0, 0x42, 0x9c, 0x60, 0xcd, 0xd1, 0x5b, 0x2d, 0xea, 0x88, 0xca, 0xcf, 0x3e, 0x92, 0xde, 0x4e, - 0xfc, 0x57, 0x90, 0x3c, 0x99, 0x4e, 0xe2, 0x33, 0x8a, 0x71, 0x54, 0xbf, 0x90, 0x85, 0xfe, 0x59, - 0xea, 0xe9, 0x86, 0xee, 0xe9, 0xe4, 0x1c, 0xf4, 0x56, 0x2c, 0x83, 0xde, 0x57, 0x32, 0x0f, 0x67, - 0x2e, 0xe5, 0xc6, 0x0b, 0x9b, 0x1b, 0xc5, 0x2c, 0x35, 0x35, 0x0e, 0x24, 0xe7, 0x21, 0xbf, 0xb8, - 0xde, 0xa2, 0x4a, 0xf6, 0xe1, 0xcc, 0xa5, 0x81, 0xf1, 0x81, 0xcd, 0x8d, 0x62, 0x2f, 0xca, 0x42, - 0x43, 0x30, 0x79, 0x04, 0xb2, 0x95, 0xb2, 0x92, 0x43, 0xe4, 0xd8, 0xe6, 0x46, 0x71, 0xb8, 0x6d, - 0x1a, 0x4f, 0xd8, 0x4d, 0xd3, 0xa3, 0xcd, 0x96, 0xb7, 0xae, 0x65, 0x2b, 0x65, 0x72, 0x11, 0xf2, - 0x13, 0xb6, 0x41, 0x95, 0x3c, 0x12, 0x91, 0xcd, 0x8d, 0xe2, 0x48, 0xdd, 0x36, 0xa8, 0x44, 0x85, - 0x78, 0xf2, 0x2a, 0xe4, 0x17, 0xcd, 0x26, 0x55, 0x7a, 0x1f, 0xce, 0x5c, 0x1a, 0xbc, 0x76, 0xf6, - 0x0a, 0x97, 0xca, 0x15, 0x5f, 0x2a, 0x57, 0x16, 0x7d, 0xb1, 0x8d, 0x8f, 0xbe, 0xbd, 0x51, 0xec, - 0xd9, 0xdc, 0x28, 0xe6, 0x99, 0x24, 0x3f, 0xff, 0xf5, 0x62, 0x46, 0xc3, 0x92, 0xe4, 0x25, 0x18, - 0x9c, 0x68, 0xb4, 0x5d, 0x8f, 0x3a, 0x73, 0x7a, 0x93, 0x2a, 0x05, 0xac, 0xf0, 0xec, 0xe6, 0x46, - 0xf1, 0x54, 0x9d, 0x83, 0x6b, 0x96, 0xde, 0x94, 0x2b, 0x96, 0xc9, 0xd5, 0x5f, 0xc9, 0xc0, 0xb1, - 0x2a, 0x75, 0x5d, 0xd3, 0xb6, 0x02, 0xd9, 0x3c, 0x06, 0x03, 0x02, 0x54, 0x29, 0xa3, 0x7c, 0x06, - 0xc6, 0xfb, 0x36, 0x37, 0x8a, 0x39, 0xd7, 0x34, 0xb4, 0x10, 0x43, 0xde, 0x0f, 0x7d, 0x77, 0x4c, - 0x6f, 0x75, 0x76, 0xaa, 0x24, 0xe4, 0x74, 0x6a, 0x73, 0xa3, 0x48, 0xd6, 0x4c, 0x6f, 0xb5, 0xd6, - 0x5c, 0xd6, 0xa5, 0x0a, 0x7d, 0x32, 0x32, 0x03, 0xa3, 0x0b, 0x8e, 0x79, 0x4f, 0xf7, 0xe8, 0x4d, - 0xba, 0xbe, 0x60, 0x37, 0xcc, 0xfa, 0xba, 0x90, 0xe2, 0xc3, 0x9b, 0x1b, 0xc5, 0x73, 0x2d, 0x8e, - 0xab, 0xdd, 0xa5, 0xeb, 0xb5, 0x16, 0x62, 0x25, 0x26, 0x89, 0x92, 0xea, 0x57, 0x7b, 0x61, 0xe8, - 0x96, 0x4b, 0x9d, 0xa0, 0xdd, 0x17, 0x21, 0xcf, 0x7e, 0x8b, 0x26, 0xa3, 0xcc, 0xdb, 0x2e, 0x75, - 0x64, 0x99, 0x33, 0x3c, 0xb9, 0x0c, 0xbd, 0x33, 0xf6, 0x8a, 0x69, 0x89, 0x66, 0x1f, 0xdf, 0xdc, - 0x28, 0x1e, 0x6b, 0x30, 0x80, 0x44, 0xc9, 0x29, 0xc8, 0x07, 0x61, 0xa8, 0xd2, 0x64, 0x3a, 0x64, - 0x5b, 0xba, 0x67, 0x3b, 0xa2, 0xb5, 0x28, 0x5d, 0x53, 0x82, 0x4b, 0x05, 0x23, 0xf4, 0xe4, 0x05, - 0x80, 0xd2, 0x9d, 0xaa, 0x66, 0x37, 0x68, 0x49, 0x9b, 0x13, 0xca, 0x80, 0xa5, 0xf5, 0x35, 0xb7, - 0xe6, 0xd8, 0x0d, 0x5a, 0xd3, 0x1d, 0xb9, 0x5a, 0x89, 0x9a, 0x4c, 0xc2, 0x48, 0xa9, 0x5e, 0xa7, - 0xae, 0xab, 0xd1, 0x4f, 0xb4, 0xa9, 0xeb, 0xb9, 0x4a, 0xef, 0xc3, 0xb9, 0x4b, 0x03, 0xe3, 0xe7, - 0x37, 0x37, 0x8a, 0x67, 0x74, 0xc4, 0xd4, 0x1c, 0x81, 0x92, 0x58, 0xc4, 0x0a, 0x91, 0x71, 0x18, - 0x2e, 0xbd, 0xd5, 0x76, 0x68, 0xc5, 0xa0, 0x96, 0x67, 0x7a, 0xeb, 0x42, 0x43, 0xce, 0x6d, 0x6e, - 0x14, 0x15, 0x9d, 0x21, 0x6a, 0xa6, 0xc0, 0x48, 0x4c, 0xa2, 0x45, 0xc8, 0x3c, 0x8c, 0xdd, 0x98, - 0x58, 0xa8, 0x52, 0xe7, 0x9e, 0x59, 0xa7, 0xa5, 0x7a, 0xdd, 0x6e, 0x5b, 0x9e, 0xd2, 0x87, 0x7c, - 0x1e, 0xd9, 0xdc, 0x28, 0x9e, 0x5f, 0xa9, 0xb7, 0x6a, 0x2e, 0xc7, 0xd6, 0x74, 0x8e, 0x96, 0x98, - 0x25, 0xcb, 0x92, 0x0f, 0xc3, 0xf0, 0xa2, 0xc3, 0xb4, 0xd0, 0x28, 0x53, 0x06, 0x57, 0xfa, 0x51, - 0xff, 0x4f, 0x5d, 0x11, 0x13, 0x10, 0x87, 0xfa, 0x3d, 0xcb, 0x1b, 0xeb, 0xf1, 0x02, 0x35, 0x03, - 0x71, 0x72, 0x63, 0x23, 0xac, 0x08, 0x05, 0x85, 0x7d, 0xbc, 0xe9, 0x50, 0x23, 0xa1, 0x6d, 0x03, - 0xd8, 0xe6, 0xcb, 0x9b, 0x1b, 0xc5, 0xc7, 0x1c, 0x41, 0x53, 0xeb, 0xaa, 0x76, 0x1d, 0x59, 0x91, - 0x49, 0xe8, 0x67, 0xda, 0x74, 0xd3, 0xb4, 0x0c, 0x05, 0x1e, 0xce, 0x5c, 0x1a, 0xb9, 0x36, 0xea, - 0xb7, 0xde, 0x87, 0x8f, 0x9f, 0xde, 0xdc, 0x28, 0x1e, 0x67, 0x3a, 0x58, 0xbb, 0x6b, 0x5a, 0xf2, - 0x14, 0x11, 0x14, 0x55, 0xff, 0x38, 0x0f, 0x23, 0x4c, 0x38, 0x92, 0x1e, 0x97, 0xd8, 0x90, 0x64, - 0x10, 0x36, 0x42, 0xdd, 0x96, 0x5e, 0xa7, 0x42, 0xa5, 0x91, 0x9d, 0xe5, 0x03, 0x25, 0x76, 0x71, - 0x7a, 0x72, 0x19, 0xfa, 0x39, 0xa8, 0x52, 0x16, 0x5a, 0x3e, 0xbc, 0xb9, 0x51, 0x1c, 0x70, 0x11, - 0x56, 0x33, 0x0d, 0x2d, 0x40, 0x33, 0x35, 0xe3, 0x7f, 0x4f, 0xdb, 0xae, 0xc7, 0x98, 0x0b, 0x25, - 0x47, 0x35, 0x13, 0x05, 0x56, 0x05, 0x4a, 0x56, 0xb3, 0x68, 0x21, 0xf2, 0x3c, 0x00, 0x87, 0x94, - 0x0c, 0xc3, 0x11, 0x9a, 0x7e, 0x66, 0x73, 0xa3, 0x78, 0x52, 0xb0, 0xd0, 0x0d, 0x43, 0x1e, 0x26, - 0x12, 0x31, 0x69, 0xc2, 0x10, 0xff, 0x35, 0xa3, 0x2f, 0xd1, 0x06, 0x57, 0xf3, 0xc1, 0x6b, 0x97, - 0x7c, 0x69, 0x46, 0xa5, 0x73, 0x45, 0x26, 0x9d, 0xb4, 0x3c, 0x67, 0x7d, 0xbc, 0x28, 0x66, 0xc6, - 0xd3, 0xa2, 0xaa, 0x06, 0xe2, 0xe4, 0x31, 0x29, 0x97, 0x61, 0x13, 0xe6, 0x94, 0xed, 0xac, 0xe9, - 0x8e, 0x41, 0x8d, 0xf1, 0x75, 0x79, 0xc2, 0x5c, 0xf6, 0xc1, 0xb5, 0x25, 0x59, 0x07, 0x64, 0x72, - 0x32, 0x01, 0xc3, 0x9c, 0x5b, 0xb5, 0xbd, 0x84, 0x7d, 0xdf, 0x97, 0x90, 0x96, 0xdb, 0x5e, 0x8a, - 0xf7, 0x77, 0xb4, 0x0c, 0x1b, 0x93, 0x1c, 0x70, 0x9b, 0x3a, 0x6c, 0x36, 0x45, 0xf5, 0x17, 0x63, - 0x52, 0x30, 0xb9, 0xc7, 0x31, 0x49, 0x1e, 0xa2, 0xc8, 0xd9, 0x57, 0x60, 0x2c, 0x21, 0x0a, 0x32, - 0x0a, 0xb9, 0xbb, 0x74, 0x9d, 0xab, 0x8b, 0xc6, 0xfe, 0x24, 0x27, 0xa0, 0xf7, 0x9e, 0xde, 0x68, - 0x8b, 0xb5, 0x4c, 0xe3, 0x3f, 0x5e, 0xc8, 0x7e, 0x20, 0xc3, 0xa6, 0x7e, 0x32, 0x61, 0x5b, 0x16, - 0xad, 0x7b, 0xf2, 0xec, 0xff, 0x2c, 0x0c, 0xcc, 0xd8, 0x75, 0xbd, 0x81, 0xfd, 0xc8, 0xf5, 0x4e, - 0xd9, 0xdc, 0x28, 0x9e, 0x60, 0x1d, 0x78, 0xa5, 0xc1, 0x30, 0x52, 0x9b, 0x42, 0x52, 0xa6, 0x00, - 0x1a, 0x6d, 0xda, 0x1e, 0xc5, 0x82, 0xd9, 0x50, 0x01, 0xb0, 0xa0, 0x83, 0x28, 0x59, 0x01, 0x42, - 0x62, 0x72, 0x15, 0xfa, 0x17, 0xd8, 0x82, 0x57, 0xb7, 0x1b, 0x42, 0xf9, 0x70, 0x4e, 0xc6, 0x45, - 0x50, 0x1e, 0x34, 0x3e, 0x91, 0x3a, 0x0d, 0x23, 0x13, 0x0d, 0x93, 0x5a, 0x9e, 0xdc, 0x6a, 0x36, - 0xa4, 0x4a, 0x2b, 0xd4, 0xf2, 0xe4, 0x56, 0xe3, 0xe0, 0xd3, 0x19, 0x54, 0x6e, 0x75, 0x40, 0xaa, - 0xfe, 0xd3, 0x1c, 0x9c, 0xb9, 0xd9, 0x5e, 0xa2, 0x8e, 0x45, 0x3d, 0xea, 0x8a, 0x95, 0x31, 0xe0, - 0x3a, 0x07, 0x63, 0x09, 0xa4, 0xe0, 0x8e, 0x2b, 0xd6, 0xdd, 0x00, 0x59, 0x13, 0x8b, 0xad, 0x3c, - 0xed, 0x25, 0x8a, 0x92, 0x69, 0x38, 0x16, 0x02, 0x59, 0x23, 0x5c, 0x25, 0x8b, 0x73, 0xfa, 0x85, - 0xcd, 0x8d, 0xe2, 0x59, 0x89, 0x1b, 0x6b, 0xb6, 0xac, 0xc1, 0xf1, 0x62, 0xe4, 0x26, 0x8c, 0x86, - 0xa0, 0x1b, 0x8e, 0xdd, 0x6e, 0xb9, 0x4a, 0x0e, 0x59, 0x15, 0x37, 0x37, 0x8a, 0x0f, 0x49, 0xac, - 0x56, 0x10, 0x29, 0xaf, 0xa4, 0xf1, 0x82, 0xe4, 0x3b, 0x33, 0x32, 0x37, 0x31, 0x0a, 0xf3, 0x38, - 0x0a, 0x9f, 0xf3, 0x47, 0x61, 0x47, 0x21, 0x5d, 0x89, 0x97, 0x14, 0x83, 0x32, 0xd6, 0x8c, 0xc4, - 0xa0, 0x4c, 0xd4, 0x78, 0x76, 0x02, 0x4e, 0xa6, 0xf2, 0xda, 0x91, 0x56, 0xff, 0x61, 0x4e, 0xe6, - 0xb2, 0x60, 0x1b, 0x41, 0x67, 0xce, 0xcb, 0x9d, 0xb9, 0x60, 0x1b, 0x68, 0x2e, 0x65, 0xc2, 0x45, - 0x4c, 0x6a, 0x6c, 0xcb, 0x36, 0xe2, 0x56, 0x53, 0xb2, 0x2c, 0xf9, 0x38, 0x9c, 0x4a, 0x00, 0xf9, - 0x74, 0xcd, 0xb5, 0xff, 0xe2, 0xe6, 0x46, 0x51, 0x4d, 0xe1, 0x1a, 0x9f, 0xbd, 0x3b, 0x70, 0x21, - 0x3a, 0x9c, 0x96, 0xa4, 0x6e, 0x5b, 0x9e, 0x6e, 0x5a, 0xc2, 0xca, 0xe3, 0xa3, 0xe4, 0xbd, 0x9b, - 0x1b, 0xc5, 0x47, 0x65, 0x1d, 0xf4, 0x69, 0xe2, 0x8d, 0xef, 0xc4, 0x87, 0x18, 0xa0, 0xa4, 0xa0, - 0x2a, 0x4d, 0x7d, 0xc5, 0x37, 0x5d, 0x2f, 0x6d, 0x6e, 0x14, 0xdf, 0x93, 0x5a, 0x87, 0xc9, 0xa8, - 0xe4, 0xa5, 0xb2, 0x13, 0x27, 0xa2, 0x01, 0x09, 0x71, 0x73, 0xb6, 0x41, 0xf1, 0x1b, 0x7a, 0x91, - 0xbf, 0xba, 0xb9, 0x51, 0xbc, 0x20, 0xf1, 0xb7, 0x6c, 0x83, 0xc6, 0x9b, 0x9f, 0x52, 0x5a, 0xfd, - 0x95, 0x1c, 0x5c, 0xa8, 0x96, 0x66, 0x67, 0x2a, 0x86, 0x6f, 0x5b, 0x2c, 0x38, 0xf6, 0x3d, 0xd3, - 0x90, 0x46, 0xef, 0x12, 0x9c, 0x8e, 0xa1, 0x26, 0xd1, 0x9c, 0x09, 0xac, 0x5a, 0xfc, 0x36, 0xdf, - 0x6e, 0x69, 0x09, 0x9a, 0x1a, 0xb7, 0x79, 0x6a, 0x11, 0x93, 0xbe, 0x13, 0x23, 0xd6, 0x47, 0x31, - 0x54, 0x75, 0xd5, 0x76, 0xbc, 0x7a, 0xdb, 0x13, 0x4a, 0x80, 0x7d, 0x94, 0xa8, 0xc3, 0x15, 0x44, - 0x5d, 0xaa, 0xf0, 0xf9, 0x90, 0x4f, 0x67, 0x60, 0xb4, 0xe4, 0x79, 0x8e, 0xb9, 0xd4, 0xf6, 0xe8, - 0xac, 0xde, 0x6a, 0x99, 0xd6, 0x0a, 0x8e, 0xf5, 0xc1, 0x6b, 0x2f, 0x05, 0x6b, 0x64, 0x57, 0x49, - 0x5c, 0x89, 0x17, 0x97, 0x86, 0xa8, 0xee, 0xa3, 0x6a, 0x4d, 0x8e, 0x93, 0x87, 0x68, 0xbc, 0x1c, - 0x1b, 0xa2, 0xa9, 0xbc, 0x76, 0x34, 0x44, 0xbf, 0x90, 0x83, 0x73, 0xf3, 0x77, 0x3d, 0x5d, 0xa3, - 0xae, 0xdd, 0x76, 0xea, 0xd4, 0xbd, 0xd5, 0x32, 0x74, 0x8f, 0x86, 0x23, 0xb5, 0x08, 0xbd, 0x25, - 0xc3, 0xa0, 0x06, 0xb2, 0xeb, 0xe5, 0xfb, 0x2f, 0x9d, 0x01, 0x34, 0x0e, 0x27, 0x8f, 0x41, 0x9f, - 0x28, 0x83, 0xdc, 0x7b, 0xc7, 0x07, 0x37, 0x37, 0x8a, 0x7d, 0x6d, 0x0e, 0xd2, 0x7c, 0x1c, 0x23, - 0x2b, 0xd3, 0x06, 0x65, 0x64, 0xb9, 0x90, 0xcc, 0xe0, 0x20, 0xcd, 0xc7, 0x91, 0xd7, 0x61, 0x04, - 0xd9, 0x06, 0xed, 0x11, 0x73, 0xdf, 0x09, 0x5f, 0xba, 0x72, 0x63, 0xf9, 0xd2, 0x84, 0xad, 0xa9, - 0x39, 0x7e, 0x01, 0x2d, 0xc6, 0x80, 0xdc, 0x81, 0x51, 0xd1, 0x88, 0x90, 0x69, 0x6f, 0x17, 0xa6, - 0x27, 0x37, 0x37, 0x8a, 0x63, 0xa2, 0xfd, 0x12, 0xdb, 0x04, 0x13, 0xc6, 0x58, 0x34, 0x3b, 0x64, - 0x5c, 0xd8, 0x8a, 0xb1, 0xf8, 0x62, 0x99, 0x71, 0x9c, 0x89, 0xfa, 0x06, 0x0c, 0xc9, 0x05, 0xc9, - 0x29, 0xdc, 0xe3, 0xf2, 0x71, 0x82, 0xbb, 0x63, 0xd3, 0xc0, 0x8d, 0xed, 0x53, 0x30, 0x58, 0xa6, - 0x6e, 0xdd, 0x31, 0x5b, 0xcc, 0x6a, 0x10, 0x4a, 0x7e, 0x6c, 0x73, 0xa3, 0x38, 0x68, 0x84, 0x60, - 0x4d, 0xa6, 0x51, 0xff, 0x5b, 0x06, 0x4e, 0x31, 0xde, 0x25, 0xd7, 0x35, 0x57, 0xac, 0xa6, 0xbc, - 0x6c, 0x3f, 0x01, 0x85, 0x2a, 0xd6, 0x27, 0x6a, 0x3a, 0xb1, 0xb9, 0x51, 0x1c, 0xe5, 0x2d, 0x90, - 0xf4, 0x50, 0xd0, 0x04, 0x1b, 0xbc, 0xec, 0x16, 0x1b, 0x3c, 0x66, 0xd2, 0x7a, 0xba, 0xe3, 0x99, - 0xd6, 0x4a, 0xd5, 0xd3, 0xbd, 0xb6, 0x1b, 0x31, 0x69, 0x05, 0xa6, 0xe6, 0x22, 0x2a, 0x62, 0xd2, - 0x46, 0x0a, 0x91, 0x57, 0x60, 0x68, 0xd2, 0x32, 0x42, 0x26, 0x7c, 0x42, 0x7c, 0x88, 0x59, 0x9a, - 0x14, 0xe1, 0x49, 0x16, 0x91, 0x02, 0xea, 0x5f, 0xcf, 0x80, 0xc2, 0x77, 0x63, 0x33, 0xa6, 0xeb, - 0xcd, 0xd2, 0xe6, 0x92, 0x34, 0x3b, 0x4d, 0xf9, 0xdb, 0x3b, 0x86, 0x93, 0xd6, 0x22, 0x34, 0x05, - 0xc4, 0xf6, 0xae, 0x61, 0xba, 0x5e, 0x7c, 0x32, 0x8c, 0x95, 0x22, 0x15, 0xe8, 0xe3, 0x9c, 0xb9, - 0x2d, 0x31, 0x78, 0x4d, 0xf1, 0x15, 0x21, 0x5e, 0x35, 0x57, 0x86, 0x26, 0x27, 0x96, 0xf7, 0xe7, - 0xa2, 0xbc, 0xfa, 0x37, 0xb2, 0x30, 0x1a, 0x2f, 0x44, 0xee, 0x40, 0xff, 0x6b, 0xb6, 0x69, 0x51, - 0x63, 0xde, 0xc2, 0x16, 0x76, 0x3f, 0xa5, 0xf0, 0x6d, 0xf1, 0xe3, 0x6f, 0x62, 0x99, 0x9a, 0x6c, - 0xc1, 0xe2, 0xa1, 0x45, 0xc0, 0x8c, 0x7c, 0x18, 0x06, 0x98, 0x0d, 0x78, 0x0f, 0x39, 0x67, 0xb7, - 0xe4, 0xfc, 0xb0, 0xe0, 0x7c, 0xc2, 0xe1, 0x85, 0x92, 0xac, 0x43, 0x76, 0x4c, 0xaf, 0x34, 0xaa, - 0xbb, 0xb6, 0x25, 0x7a, 0x1e, 0xf5, 0xca, 0x41, 0x88, 0xac, 0x57, 0x9c, 0x86, 0x99, 0xae, 0xfc, - 0x63, 0xb1, 0x1b, 0xa4, 0xbd, 0x0b, 0x97, 0x55, 0xbc, 0x07, 0x24, 0x62, 0xf5, 0xbb, 0xb3, 0xf0, - 0x64, 0x28, 0x32, 0x8d, 0xde, 0x33, 0xe9, 0x9a, 0x10, 0xe7, 0xaa, 0xd9, 0x12, 0x9b, 0x47, 0xa6, - 0xf2, 0xee, 0xc4, 0xaa, 0x6e, 0xad, 0x50, 0x83, 0x5c, 0x86, 0x5e, 0xb6, 0xc3, 0x77, 0x95, 0x0c, - 0x9a, 0x6b, 0x38, 0x9d, 0x38, 0x0c, 0x20, 0x9f, 0x3e, 0x20, 0x05, 0xb1, 0xa1, 0xb0, 0xe8, 0xe8, - 0xa6, 0xe7, 0xf7, 0x6c, 0x29, 0xd9, 0xb3, 0xdb, 0xa8, 0xf1, 0x0a, 0xe7, 0xc1, 0xe7, 0x7c, 0x14, - 0x84, 0x87, 0x00, 0x59, 0x10, 0x9c, 0xe4, 0xec, 0xf3, 0x30, 0x28, 0x11, 0xef, 0x68, 0x52, 0xff, - 0x72, 0x5e, 0xd6, 0x75, 0xbf, 0x59, 0x42, 0xd7, 0xaf, 0x32, 0x1d, 0x75, 0x5d, 0x66, 0x55, 0x70, - 0x25, 0x17, 0x9a, 0x88, 0xa0, 0xa8, 0x26, 0x22, 0x88, 0x3c, 0x0d, 0xfd, 0x9c, 0x45, 0xb0, 0x7f, - 0xc5, 0xbd, 0xaf, 0x83, 0xb0, 0xe8, 0xd2, 0x1c, 0x10, 0x92, 0x9f, 0xcd, 0xc0, 0xf9, 0xae, 0x92, - 0x40, 0x65, 0x18, 0xbc, 0xf6, 0xcc, 0xae, 0xc4, 0x38, 0xfe, 0xe4, 0xe6, 0x46, 0xf1, 0x72, 0x33, - 0x20, 0xa9, 0x39, 0x12, 0x4d, 0xad, 0xce, 0x89, 0xa4, 0x76, 0x75, 0x6f, 0x0a, 0x33, 0x1e, 0x79, - 0xa5, 0x53, 0x78, 0x86, 0x63, 0xd5, 0xd7, 0xfd, 0x46, 0xe6, 0x43, 0xe3, 0x51, 0x7c, 0xef, 0xb2, - 0x4f, 0x92, 0x52, 0x4d, 0x07, 0x2e, 0xa4, 0x0e, 0xa7, 0x39, 0xa6, 0xac, 0xaf, 0xcf, 0x2f, 0xcf, - 0xda, 0x96, 0xb7, 0xea, 0x57, 0xd0, 0x2b, 0x1f, 0x82, 0x60, 0x05, 0x86, 0xbe, 0x5e, 0xb3, 0x97, - 0x6b, 0x4d, 0x46, 0x95, 0x52, 0x47, 0x27, 0x4e, 0x6c, 0xa2, 0x15, 0x63, 0xce, 0x9f, 0x82, 0x0a, - 0xe1, 0x11, 0x95, 0x3f, 0x4e, 0x93, 0x13, 0x4e, 0xac, 0x90, 0x5a, 0x81, 0xa1, 0x19, 0xbb, 0x7e, - 0x37, 0x50, 0x97, 0xe7, 0xa1, 0xb0, 0xa8, 0x3b, 0x2b, 0xd4, 0x43, 0x59, 0x0c, 0x5e, 0x1b, 0xbb, - 0xc2, 0x8f, 0x7d, 0x19, 0x11, 0x47, 0x8c, 0x8f, 0x88, 0xd9, 0xa0, 0xe0, 0xe1, 0x6f, 0x4d, 0x14, - 0x50, 0xbf, 0xde, 0x0b, 0x43, 0xe2, 0x88, 0x12, 0x67, 0x73, 0xf2, 0x42, 0x78, 0xe8, 0x2b, 0xa6, - 0xaf, 0xe0, 0x98, 0x26, 0x38, 0x5e, 0x1a, 0x62, 0xcc, 0x7e, 0x6b, 0xa3, 0x98, 0xd9, 0xdc, 0x28, - 0xf6, 0x68, 0xfd, 0xd2, 0xa6, 0x32, 0x5c, 0x6f, 0xa4, 0x05, 0x56, 0x3e, 0x74, 0x8c, 0x95, 0xe5, - 0xeb, 0xcf, 0x2b, 0xd0, 0x27, 0xda, 0x20, 0x34, 0xee, 0x74, 0x78, 0x96, 0x11, 0x39, 0x6a, 0x8d, - 0x95, 0xf6, 0x4b, 0x91, 0x97, 0xa0, 0xc0, 0xf7, 0xf6, 0x42, 0x00, 0xa7, 0xd2, 0xcf, 0x42, 0x62, - 0xc5, 0x45, 0x19, 0x32, 0x0d, 0x10, 0xee, 0xeb, 0x83, 0x93, 0x65, 0xc1, 0x21, 0xb9, 0xe3, 0x8f, - 0x71, 0x91, 0xca, 0x92, 0x67, 0x61, 0x68, 0x91, 0x3a, 0x4d, 0xd3, 0xd2, 0x1b, 0x55, 0xf3, 0x2d, - 0xff, 0x70, 0x19, 0x17, 0x5e, 0xd7, 0x7c, 0x4b, 0x1e, 0xb9, 0x11, 0x3a, 0xf2, 0xb1, 0xb4, 0x7d, - 0x73, 0x1f, 0x36, 0xe4, 0x91, 0x2d, 0x37, 0x94, 0xb1, 0xf6, 0xa4, 0x6c, 0xa3, 0x5f, 0x87, 0xe1, - 0xc8, 0x96, 0x49, 0x9c, 0x1e, 0x9e, 0x4f, 0xb2, 0x96, 0xf6, 0x7f, 0x31, 0xb6, 0x51, 0x0e, 0x4c, - 0x93, 0x2b, 0x96, 0xe9, 0x99, 0x7a, 0x63, 0xc2, 0x6e, 0x36, 0x75, 0xcb, 0x50, 0x06, 0x42, 0x4d, - 0x36, 0x39, 0xa6, 0x56, 0xe7, 0x28, 0x59, 0x93, 0xa3, 0x85, 0xd8, 0xb6, 0x5c, 0xf4, 0xa1, 0x46, - 0xeb, 0xb6, 0xc3, 0x6c, 0x01, 0x3c, 0x1c, 0x14, 0xdb, 0x72, 0x97, 0xe3, 0x6a, 0x8e, 0x8f, 0x94, - 0x8d, 0xed, 0x78, 0xc1, 0xd7, 0xf2, 0xfd, 0x83, 0xa3, 0x43, 0xf1, 0xf3, 0x5c, 0xf5, 0xaf, 0xe5, - 0x60, 0x50, 0x90, 0xb2, 0xa5, 0xf4, 0x48, 0xc1, 0xf7, 0xa2, 0xe0, 0xa9, 0x8a, 0x5a, 0xd8, 0x2f, - 0x45, 0x55, 0x3f, 0x93, 0x0d, 0x66, 0xa3, 0x05, 0xc7, 0xb4, 0xf6, 0x36, 0x1b, 0x5d, 0x04, 0x98, - 0x58, 0x6d, 0x5b, 0x77, 0xf9, 0xbd, 0x55, 0x36, 0xbc, 0xb7, 0xaa, 0x9b, 0x9a, 0x84, 0x21, 0xe7, - 0x21, 0x5f, 0x66, 0xfc, 0x59, 0xcf, 0x0c, 0x8d, 0x0f, 0xbc, 0xcd, 0x39, 0x65, 0x9e, 0xd4, 0x10, - 0xcc, 0x36, 0x57, 0xe3, 0xeb, 0x1e, 0xe5, 0xe6, 0x6c, 0x8e, 0x6f, 0xae, 0x96, 0x18, 0x40, 0xe3, - 0x70, 0x72, 0x1d, 0xc6, 0xca, 0xb4, 0xa1, 0xaf, 0xcf, 0x9a, 0x8d, 0x86, 0xe9, 0xd2, 0xba, 0x6d, - 0x19, 0x2e, 0x0a, 0x59, 0x54, 0xd7, 0x74, 0xb5, 0x24, 0x01, 0x51, 0xa1, 0x30, 0xbf, 0xbc, 0xec, - 0x52, 0x0f, 0xc5, 0x97, 0x1b, 0x07, 0x36, 0x39, 0xdb, 0x08, 0xd1, 0x04, 0x46, 0xfd, 0x85, 0x0c, - 0xdb, 0xbd, 0xb8, 0x77, 0x3d, 0xbb, 0x15, 0x68, 0xf9, 0x9e, 0x44, 0x72, 0x39, 0xb4, 0x2b, 0xb2, - 0xf8, 0xb5, 0xc7, 0xc4, 0xd7, 0xf6, 0x09, 0xdb, 0x22, 0xb4, 0x28, 0x52, 0xbf, 0x2a, 0xb7, 0xc5, - 0x57, 0xa9, 0x7f, 0x94, 0x85, 0xd3, 0xa2, 0xc5, 0x13, 0x0d, 0xb3, 0xb5, 0x64, 0xeb, 0x8e, 0xa1, - 0xd1, 0x3a, 0x35, 0xef, 0xd1, 0xc3, 0x39, 0xf0, 0xa2, 0x43, 0x27, 0xbf, 0x87, 0xa1, 0x73, 0x0d, - 0x37, 0x82, 0x4c, 0x32, 0x78, 0xe0, 0xcb, 0x8d, 0x8a, 0xd1, 0xcd, 0x8d, 0xe2, 0x90, 0xc1, 0xc1, - 0x78, 0xe4, 0xaf, 0xc9, 0x44, 0x4c, 0x49, 0x66, 0xa8, 0xb5, 0xe2, 0xad, 0xa2, 0x92, 0xf4, 0x72, - 0x25, 0x69, 0x20, 0x44, 0x13, 0x18, 0xf5, 0x3f, 0x66, 0xe1, 0x44, 0x5c, 0xe4, 0x55, 0x6a, 0x19, - 0x47, 0xf2, 0x7e, 0x67, 0xe4, 0xfd, 0xcd, 0x1c, 0x3c, 0x24, 0xca, 0x54, 0x57, 0x75, 0x87, 0x1a, - 0x65, 0xd3, 0xa1, 0x75, 0xcf, 0x76, 0xd6, 0x0f, 0xb1, 0x01, 0xb5, 0x7f, 0x62, 0xbf, 0x0e, 0x05, - 0xb1, 0xfd, 0xe7, 0xeb, 0xcc, 0x48, 0xd0, 0x12, 0x84, 0x26, 0x56, 0x28, 0x7e, 0x74, 0x10, 0xeb, - 0xac, 0xc2, 0x76, 0x3a, 0xeb, 0x03, 0x30, 0x1c, 0x88, 0x1e, 0x37, 0xa2, 0x7d, 0xa1, 0xb5, 0x65, - 0xf8, 0x08, 0xdc, 0x8b, 0x6a, 0x51, 0x42, 0xac, 0xcd, 0x07, 0x54, 0xca, 0x68, 0x0d, 0x0d, 0x8b, - 0xda, 0x82, 0x72, 0xa6, 0xa1, 0xc9, 0x44, 0xea, 0x46, 0x1e, 0xce, 0xa6, 0x77, 0xbb, 0x46, 0x75, - 0xe3, 0xa8, 0xd7, 0xbf, 0x25, 0x7b, 0x9d, 0x3c, 0x02, 0xf9, 0x05, 0xdd, 0x5b, 0x15, 0xf7, 0xe0, - 0x78, 0x27, 0xbc, 0x6c, 0x36, 0x68, 0xad, 0xa5, 0x7b, 0xab, 0x1a, 0xa2, 0xa4, 0x39, 0x03, 0x90, - 0x63, 0xca, 0x9c, 0x21, 0x2d, 0xf6, 0x83, 0x0f, 0x67, 0x2e, 0xe5, 0x53, 0x17, 0xfb, 0xaf, 0xe7, - 0x3b, 0xcd, 0x2b, 0x77, 0x1c, 0xd3, 0xa3, 0x47, 0x1a, 0x76, 0xa4, 0x61, 0x7b, 0xd4, 0xb0, 0xdf, - 0xc9, 0xc2, 0x70, 0xb0, 0x69, 0x7a, 0x93, 0xd6, 0x0f, 0x66, 0xad, 0x0a, 0xb7, 0x32, 0xb9, 0x3d, - 0x6f, 0x65, 0xf6, 0xa2, 0x50, 0x6a, 0x70, 0xe4, 0xc9, 0x4d, 0x03, 0x94, 0x18, 0x3f, 0xf2, 0x0c, - 0x0e, 0x3a, 0x1f, 0x81, 0xbe, 0x59, 0xfd, 0xbe, 0xd9, 0x6c, 0x37, 0x85, 0x95, 0x8e, 0x7e, 0x5d, - 0x4d, 0xfd, 0xbe, 0xe6, 0xc3, 0xd5, 0x7f, 0x9e, 0x81, 0x11, 0x21, 0x54, 0xc1, 0x7c, 0x4f, 0x52, - 0x0d, 0xa5, 0x93, 0xdd, 0xb3, 0x74, 0x72, 0xbb, 0x97, 0x8e, 0xfa, 0xa3, 0x39, 0x50, 0xa6, 0xcc, - 0x06, 0x5d, 0x74, 0x74, 0xcb, 0x5d, 0xa6, 0x8e, 0xd8, 0x4e, 0x4f, 0x32, 0x56, 0x7b, 0xfa, 0x40, - 0x69, 0x4a, 0xc9, 0xee, 0x6a, 0x4a, 0x79, 0x1f, 0x0c, 0x88, 0xc6, 0x04, 0x3e, 0x85, 0x38, 0x6a, - 0x1c, 0x1f, 0xa8, 0x85, 0x78, 0x46, 0x5c, 0x6a, 0xb5, 0x1c, 0xfb, 0x1e, 0x75, 0xf8, 0x2d, 0x95, - 0x20, 0xd6, 0x7d, 0xa0, 0x16, 0xe2, 0x25, 0xce, 0xd4, 0xb7, 0x17, 0x65, 0xce, 0xd4, 0xd1, 0x42, - 0x3c, 0xb9, 0x04, 0xfd, 0x33, 0x76, 0x5d, 0x47, 0x41, 0xf3, 0x69, 0x65, 0x68, 0x73, 0xa3, 0xd8, - 0xdf, 0x10, 0x30, 0x2d, 0xc0, 0x32, 0xca, 0xb2, 0xbd, 0x66, 0x35, 0x6c, 0x9d, 0x3b, 0xbf, 0xf4, - 0x73, 0x4a, 0x43, 0xc0, 0xb4, 0x00, 0xcb, 0x28, 0x99, 0xcc, 0xd1, 0xa9, 0xa8, 0x3f, 0xe4, 0xb9, - 0x2c, 0x60, 0x5a, 0x80, 0x55, 0x7f, 0x21, 0xcf, 0xb4, 0xd7, 0x35, 0xdf, 0x7a, 0xe0, 0xd7, 0x85, - 0x70, 0xc0, 0xf4, 0xee, 0x62, 0xc0, 0x3c, 0x30, 0x07, 0x76, 0xea, 0x1f, 0xf7, 0x01, 0x08, 0xe9, - 0x4f, 0x1e, 0x6d, 0x0e, 0xf7, 0xa6, 0x35, 0x65, 0x18, 0x9b, 0xb4, 0x56, 0x75, 0xab, 0x4e, 0x8d, - 0xf0, 0xd8, 0xb2, 0x80, 0x43, 0x1b, 0x7d, 0x7a, 0xa9, 0x40, 0x86, 0xe7, 0x96, 0x5a, 0xb2, 0x00, - 0x79, 0x0a, 0x06, 0x2b, 0x96, 0x47, 0x1d, 0xbd, 0xee, 0x99, 0xf7, 0xa8, 0x98, 0x1a, 0xf0, 0x66, - 0xd8, 0x0c, 0xc1, 0x9a, 0x4c, 0x43, 0xae, 0xc3, 0xd0, 0x82, 0xee, 0x78, 0x66, 0xdd, 0x6c, 0xe9, - 0x96, 0xe7, 0x2a, 0xfd, 0x38, 0xa3, 0xa1, 0x85, 0xd1, 0x92, 0xe0, 0x5a, 0x84, 0x8a, 0x7c, 0x0c, - 0x06, 0x70, 0x6b, 0x8a, 0x8e, 0xd3, 0x03, 0x5b, 0x5e, 0x1c, 0x3e, 0x1a, 0xba, 0x07, 0xf2, 0xd3, - 0x57, 0xbc, 0x01, 0x8e, 0xdf, 0x1d, 0x06, 0x1c, 0xc9, 0x87, 0xa0, 0x6f, 0xd2, 0x32, 0x90, 0x39, - 0x6c, 0xc9, 0x5c, 0x15, 0xcc, 0x4f, 0x85, 0xcc, 0xed, 0x56, 0x8c, 0xb7, 0xcf, 0x2e, 0x7d, 0x94, - 0x0d, 0xbe, 0x73, 0xa3, 0x6c, 0xe8, 0x1d, 0x38, 0x16, 0x1f, 0xde, 0xaf, 0x63, 0xf1, 0x91, 0x5d, - 0x1e, 0x8b, 0xab, 0x6f, 0xc1, 0xe0, 0xf8, 0xc2, 0x54, 0x30, 0x7a, 0xcf, 0x40, 0x6e, 0x41, 0x78, - 0x2a, 0xe4, 0xb9, 0x3d, 0xd3, 0x32, 0x0d, 0x8d, 0xc1, 0xc8, 0x65, 0xe8, 0x9f, 0x40, 0xf7, 0x37, - 0x71, 0x8b, 0x98, 0xe7, 0xeb, 0x5f, 0x1d, 0x61, 0xe8, 0x05, 0xeb, 0xa3, 0xc9, 0x63, 0xd0, 0xb7, - 0xe0, 0xd8, 0x2b, 0x8e, 0xde, 0x14, 0x6b, 0x30, 0xba, 0x8a, 0xb4, 0x38, 0x48, 0xf3, 0x71, 0xea, - 0xf7, 0x67, 0x7c, 0xb3, 0x9d, 0x95, 0xa8, 0xb6, 0xf1, 0x68, 0x1e, 0xeb, 0xee, 0xe7, 0x25, 0x5c, - 0x0e, 0xd2, 0x7c, 0x1c, 0xb9, 0x0c, 0xbd, 0x93, 0x8e, 0x63, 0x3b, 0xb2, 0xb3, 0x39, 0x65, 0x00, - 0xf9, 0xba, 0x17, 0x29, 0xc8, 0x73, 0x30, 0xc8, 0xe7, 0x1c, 0x7e, 0xa2, 0x99, 0xeb, 0x76, 0x53, - 0x2a, 0x53, 0xaa, 0x5f, 0xcd, 0x49, 0x36, 0x1b, 0x97, 0xf8, 0x03, 0x78, 0x2b, 0xf0, 0x34, 0xe4, - 0xc6, 0x17, 0xa6, 0xc4, 0x04, 0x78, 0xdc, 0x2f, 0x2a, 0xa9, 0x4a, 0xac, 0x1c, 0xa3, 0x26, 0xe7, - 0x20, 0xbf, 0xc0, 0xd4, 0xa7, 0x80, 0xea, 0xd1, 0xbf, 0xb9, 0x51, 0xcc, 0xb7, 0x98, 0xfe, 0x20, - 0x14, 0xb1, 0x6c, 0x33, 0xc3, 0x77, 0x4c, 0x1c, 0x1b, 0xee, 0x63, 0xce, 0x41, 0xbe, 0xe4, 0xac, - 0xdc, 0x13, 0xb3, 0x16, 0x62, 0x75, 0x67, 0xe5, 0x9e, 0x86, 0x50, 0x72, 0x15, 0x40, 0xa3, 0x5e, - 0xdb, 0xb1, 0xf0, 0x1d, 0xc8, 0x00, 0x9e, 0xbf, 0xe1, 0x6c, 0xe8, 0x20, 0xb4, 0x56, 0xb7, 0x0d, - 0xaa, 0x49, 0x24, 0xea, 0x4f, 0x87, 0x17, 0x3b, 0x65, 0xd3, 0xbd, 0x7b, 0xd4, 0x85, 0x3b, 0xe8, - 0x42, 0x5d, 0x1c, 0x71, 0x26, 0x3b, 0xa9, 0x08, 0xbd, 0x53, 0x0d, 0x7d, 0xc5, 0xc5, 0x3e, 0x14, - 0xbe, 0x64, 0xcb, 0x0c, 0xa0, 0x71, 0x78, 0xac, 0x9f, 0xfa, 0xb7, 0xee, 0xa7, 0x2f, 0xf6, 0x06, - 0xa3, 0x6d, 0x8e, 0x7a, 0x6b, 0xb6, 0x73, 0xd4, 0x55, 0xdb, 0xed, 0xaa, 0x8b, 0xd0, 0x57, 0x75, - 0xea, 0xd2, 0xd1, 0x05, 0xee, 0x07, 0x5c, 0xa7, 0xce, 0x8f, 0x2d, 0x7c, 0x24, 0xa3, 0x2b, 0xbb, - 0x1e, 0xd2, 0xf5, 0x85, 0x74, 0x86, 0xeb, 0x09, 0x3a, 0x81, 0x14, 0x74, 0x0b, 0xb6, 0xe3, 0x89, - 0x8e, 0x0b, 0xe8, 0x5a, 0xb6, 0xe3, 0x69, 0x3e, 0x92, 0xbc, 0x0f, 0x60, 0x71, 0x62, 0xc1, 0x77, - 0xb6, 0x1f, 0x08, 0x7d, 0x01, 0x85, 0x97, 0xbd, 0x26, 0xa1, 0xc9, 0x22, 0x0c, 0xcc, 0xb7, 0xa8, - 0xc3, 0xb7, 0x42, 0xfc, 0x65, 0xc7, 0x7b, 0x63, 0xa2, 0x15, 0xfd, 0x7e, 0x45, 0xfc, 0x1f, 0x90, - 0xf3, 0xf5, 0xc5, 0xf6, 0x7f, 0x6a, 0x21, 0x23, 0xf2, 0x1c, 0x14, 0x4a, 0xdc, 0xce, 0x1b, 0x44, - 0x96, 0x81, 0xc8, 0x70, 0x0b, 0xca, 0x51, 0x7c, 0xcf, 0xae, 0xe3, 0xdf, 0x9a, 0x20, 0x57, 0x2f, - 0xc3, 0x68, 0xbc, 0x1a, 0x32, 0x08, 0x7d, 0x13, 0xf3, 0x73, 0x73, 0x93, 0x13, 0x8b, 0xa3, 0x3d, - 0xa4, 0x1f, 0xf2, 0xd5, 0xc9, 0xb9, 0xf2, 0x68, 0x46, 0xfd, 0x39, 0x69, 0x06, 0x61, 0xaa, 0x75, - 0x74, 0x35, 0xbc, 0xa7, 0xfb, 0x96, 0x51, 0xbc, 0x0f, 0xc5, 0x13, 0x83, 0xa6, 0xe9, 0x79, 0xd4, - 0x10, 0xab, 0x04, 0xde, 0x17, 0x7a, 0xf7, 0xb5, 0x04, 0x9e, 0x3c, 0x01, 0xc3, 0x08, 0x13, 0x57, - 0x84, 0x7c, 0x7f, 0x2c, 0x0a, 0x38, 0xf7, 0xb5, 0x28, 0x52, 0xfd, 0x5a, 0x78, 0x3b, 0x3c, 0x43, - 0xf5, 0xc3, 0x7a, 0xa3, 0xf8, 0x2e, 0xe9, 0x2f, 0xf5, 0xcf, 0xf2, 0xfc, 0x09, 0x08, 0x7f, 0xb8, - 0x77, 0x10, 0xa2, 0x0c, 0x8f, 0x74, 0x73, 0x3b, 0x38, 0xd2, 0x7d, 0x02, 0x0a, 0xb3, 0xd4, 0x5b, - 0xb5, 0x7d, 0xc7, 0x2f, 0xf4, 0xd0, 0x6b, 0x22, 0x44, 0xf6, 0xd0, 0xe3, 0x34, 0xe4, 0x2e, 0x10, - 0xff, 0x55, 0x5e, 0xe0, 0x88, 0xed, 0x1f, 0x21, 0x9f, 0x4e, 0xec, 0x53, 0xaa, 0xf8, 0x24, 0x17, - 0x7d, 0xec, 0x4f, 0x04, 0x8e, 0xde, 0x92, 0x27, 0xd6, 0x9f, 0x6e, 0x14, 0x0b, 0x9c, 0x46, 0x4b, - 0x61, 0x4b, 0x5e, 0x87, 0x81, 0xd9, 0xa9, 0x92, 0x78, 0xa1, 0xc7, 0xbd, 0x22, 0xce, 0x04, 0x52, - 0xf4, 0x11, 0x81, 0x48, 0xf0, 0xbd, 0x4d, 0x73, 0x59, 0x4f, 0x3e, 0xd0, 0x0b, 0xb9, 0x30, 0x6d, - 0xe1, 0x2f, 0x77, 0xc4, 0xe9, 0x42, 0xa0, 0x2d, 0xd1, 0xf7, 0x3c, 0x71, 0x59, 0x71, 0x6c, 0x4c, - 0x5b, 0xfa, 0xf7, 0x30, 0xba, 0xe7, 0x61, 0xac, 0xd4, 0x6a, 0x35, 0x4c, 0x6a, 0xa0, 0xbe, 0x68, - 0xed, 0x06, 0x75, 0x85, 0xcb, 0x0f, 0x3e, 0x06, 0xd1, 0x39, 0xb2, 0x86, 0xef, 0x42, 0x6b, 0x4e, - 0x3b, 0xea, 0x9f, 0x99, 0x2c, 0xab, 0xfe, 0x60, 0x16, 0x4e, 0x4d, 0x38, 0x54, 0xf7, 0xe8, 0xec, - 0x54, 0xa9, 0xd4, 0x46, 0x1f, 0xb9, 0x46, 0x83, 0x5a, 0x2b, 0x07, 0x33, 0xac, 0x5f, 0x84, 0x91, - 0xa0, 0x01, 0xd5, 0xba, 0xdd, 0xa2, 0xf2, 0xc3, 0xaa, 0xba, 0x8f, 0xa9, 0xb9, 0x0c, 0xa5, 0xc5, - 0x48, 0xc9, 0x4d, 0x38, 0x1e, 0x40, 0x4a, 0x8d, 0x86, 0xbd, 0xa6, 0xd1, 0xb6, 0xcb, 0x1d, 0x63, - 0xfb, 0xb9, 0x63, 0x6c, 0xc8, 0x41, 0x67, 0xf8, 0x9a, 0xc3, 0x08, 0xb4, 0xb4, 0x52, 0xea, 0x97, - 0x72, 0x70, 0xfa, 0xb6, 0xde, 0x30, 0x8d, 0x50, 0x34, 0x1a, 0x75, 0x5b, 0xb6, 0xe5, 0xd2, 0x43, - 0x34, 0x4a, 0x23, 0x43, 0x21, 0xbf, 0x2f, 0x43, 0x21, 0xd9, 0x45, 0xbd, 0x7b, 0xee, 0xa2, 0xc2, - 0xae, 0xba, 0xe8, 0x3f, 0x64, 0x60, 0xd4, 0x77, 0xfc, 0x97, 0x5f, 0x53, 0x4b, 0x5e, 0xe9, 0x78, - 0x84, 0x18, 0xf3, 0x83, 0x46, 0x3c, 0xa9, 0x42, 0xdf, 0xe4, 0xfd, 0x96, 0xe9, 0x50, 0x77, 0x1b, - 0x4e, 0xdc, 0xe7, 0xc5, 0x71, 0xc9, 0x18, 0xe5, 0x45, 0x12, 0x27, 0x25, 0x1c, 0x8c, 0xcf, 0xf9, - 0xf8, 0xd3, 0x87, 0x71, 0xff, 0x89, 0x38, 0x7f, 0xce, 0x27, 0x9e, 0x48, 0x44, 0xde, 0x67, 0x86, - 0xa4, 0xe4, 0x51, 0xc8, 0x2d, 0x2e, 0xce, 0x88, 0x99, 0x14, 0x9f, 0xe6, 0x7b, 0x9e, 0xfc, 0x5e, - 0x91, 0x61, 0xd5, 0xdf, 0xcf, 0x02, 0x30, 0x55, 0xe0, 0xc3, 0xf5, 0x40, 0x94, 0x70, 0x1c, 0xfa, - 0x7d, 0x81, 0x0b, 0x35, 0x0c, 0xbc, 0xf6, 0xe3, 0x1d, 0x11, 0xaf, 0x3b, 0x78, 0xa1, 0x51, 0xf4, - 0x1d, 0xc9, 0xf9, 0x3d, 0x00, 0xee, 0x6c, 0xd0, 0x91, 0xdc, 0x77, 0x1f, 0x7f, 0x1f, 0x0c, 0x88, - 0x19, 0xcf, 0x8e, 0x9c, 0xff, 0xd7, 0x7d, 0xa0, 0x16, 0xe2, 0x63, 0x53, 0x6b, 0x61, 0x0f, 0x0b, - 0xb1, 0x2f, 0x5e, 0xde, 0x2b, 0x47, 0xe2, 0xdd, 0x67, 0xf1, 0x7e, 0x4e, 0x88, 0x97, 0xbf, 0xe0, - 0x39, 0xb4, 0xe2, 0xdd, 0xb7, 0xb3, 0x6f, 0xf5, 0x77, 0x32, 0x40, 0x58, 0xb3, 0x16, 0x74, 0xd7, - 0x5d, 0xb3, 0x1d, 0x83, 0x3b, 0xa7, 0x1f, 0x88, 0x60, 0xf6, 0xef, 0xbe, 0xf2, 0xab, 0xfd, 0x70, - 0x3c, 0xe2, 0xf8, 0x7b, 0xc8, 0x27, 0xab, 0xcb, 0xd1, 0xd1, 0xd4, 0xed, 0xd5, 0xcb, 0x7b, 0xe4, - 0x0b, 0xd1, 0xde, 0xc8, 0x03, 0x34, 0xe9, 0x26, 0xf4, 0x49, 0x18, 0x12, 0x3f, 0xd8, 0x0a, 0xed, - 0xdf, 0x74, 0xe1, 0x28, 0x75, 0x19, 0x40, 0x8b, 0xa0, 0xc9, 0x33, 0x30, 0xc0, 0x06, 0xcc, 0x0a, - 0x46, 0xf1, 0xe8, 0x0b, 0x5f, 0x94, 0x18, 0x3e, 0x50, 0x5e, 0x4f, 0x02, 0x4a, 0xe9, 0x1d, 0x51, - 0xff, 0x36, 0xde, 0x11, 0x7d, 0x1c, 0x06, 0x4b, 0x96, 0x65, 0x7b, 0xb8, 0x49, 0x77, 0xc5, 0xd5, - 0x44, 0x47, 0xab, 0xfc, 0x51, 0x7c, 0x1c, 0x1f, 0xd2, 0xa7, 0x9a, 0xe5, 0x32, 0x43, 0x72, 0xcd, - 0x7f, 0x15, 0x43, 0x1d, 0xe1, 0x55, 0x8e, 0xd7, 0x33, 0x8e, 0x80, 0x25, 0x1f, 0xc5, 0x60, 0xe7, - 0x0d, 0x2f, 0x38, 0x76, 0xcb, 0x76, 0xa9, 0xc1, 0x05, 0x35, 0x18, 0x86, 0x1a, 0x68, 0x09, 0x04, - 0xbe, 0x63, 0x8b, 0x44, 0xd4, 0x88, 0x14, 0x21, 0xcb, 0x70, 0xc2, 0xbf, 0x28, 0x0e, 0x5e, 0x0c, - 0x56, 0xca, 0xae, 0x32, 0x84, 0xaf, 0x92, 0x48, 0x5c, 0x19, 0x2a, 0xe5, 0xf1, 0x0b, 0xfe, 0xb5, - 0x88, 0xff, 0xe4, 0xb0, 0x66, 0x1a, 0x72, 0x57, 0xa7, 0xf2, 0x23, 0xdf, 0x06, 0x83, 0xb3, 0xfa, - 0xfd, 0x72, 0x5b, 0x9c, 0xbd, 0x0c, 0x6f, 0xff, 0xf6, 0xa5, 0xa9, 0xdf, 0xaf, 0x19, 0xa2, 0x5c, - 0xcc, 0xa6, 0x90, 0x59, 0x92, 0x1a, 0x9c, 0x5a, 0x70, 0xec, 0xa6, 0xed, 0x51, 0x23, 0xf6, 0xf8, - 0xee, 0x58, 0xf8, 0x5a, 0xb7, 0x25, 0x28, 0x6a, 0x5d, 0x5e, 0xe1, 0x75, 0x60, 0x43, 0x9a, 0x70, - 0xac, 0xe4, 0xba, 0xed, 0x26, 0x0d, 0x6f, 0xa8, 0x46, 0xb7, 0xfc, 0x8c, 0xf7, 0x0a, 0xaf, 0xe5, - 0x87, 0x74, 0x2c, 0xca, 0x2f, 0xa8, 0x6a, 0x9e, 0x29, 0xd7, 0x88, 0xdf, 0x12, 0xe7, 0xfd, 0x5a, - 0xbe, 0x7f, 0x64, 0xf4, 0x98, 0x76, 0x3a, 0xd9, 0x98, 0x45, 0xd3, 0x6b, 0x50, 0xf5, 0x2b, 0x19, - 0x80, 0x50, 0xc0, 0xe4, 0xc9, 0x68, 0xa8, 0xa0, 0x4c, 0x78, 0xd1, 0x21, 0xa2, 0x17, 0x44, 0x62, - 0x03, 0x91, 0x73, 0x90, 0xc7, 0x08, 0x17, 0xd9, 0xf0, 0x60, 0xf5, 0xae, 0x69, 0x19, 0x1a, 0x42, - 0x19, 0x56, 0x7a, 0x8a, 0x8e, 0x58, 0xbc, 0xd4, 0xe7, 0x56, 0x61, 0x19, 0x8e, 0x55, 0xdb, 0x4b, - 0x7e, 0xdd, 0xd2, 0xbb, 0x3a, 0x0c, 0xb4, 0xe1, 0xb6, 0x97, 0x82, 0xc7, 0xa8, 0x91, 0x30, 0x26, - 0xd1, 0x22, 0xea, 0x2f, 0x64, 0x62, 0xb3, 0xe0, 0x01, 0x2e, 0x7a, 0xef, 0x49, 0xfa, 0x69, 0x24, - 0xa7, 0x25, 0xf5, 0xc7, 0xb2, 0x30, 0xb8, 0x60, 0x3b, 0x9e, 0x08, 0x19, 0x72, 0xb8, 0x57, 0x21, - 0x69, 0xaf, 0x94, 0xdf, 0xc1, 0x5e, 0xe9, 0x1c, 0xe4, 0x25, 0x17, 0x65, 0x7e, 0x2f, 0x62, 0x18, - 0x8e, 0x86, 0x50, 0xf5, 0xdb, 0xb3, 0x00, 0x1f, 0x7a, 0xea, 0xa9, 0x07, 0x58, 0x40, 0xea, 0x8f, - 0x64, 0xe0, 0x98, 0xb8, 0xa8, 0x93, 0x82, 0x6e, 0xf5, 0xf9, 0x57, 0xac, 0xf2, 0xb8, 0xe4, 0x20, - 0xcd, 0xc7, 0xb1, 0x25, 0x60, 0xf2, 0xbe, 0xe9, 0xe1, 0x5d, 0x85, 0x14, 0x75, 0x8b, 0x0a, 0x98, - 0xbc, 0x04, 0xf8, 0x74, 0xe4, 0x49, 0xff, 0x0a, 0x32, 0x17, 0xae, 0x7b, 0xac, 0xc0, 0x64, 0xea, - 0x35, 0xa4, 0xfa, 0x4b, 0x79, 0xc8, 0x4f, 0xde, 0xa7, 0xf5, 0x43, 0xde, 0x35, 0xd2, 0xc1, 0x66, - 0x7e, 0x8f, 0x07, 0x9b, 0xbb, 0xf1, 0xa9, 0x78, 0x25, 0xec, 0xcf, 0x42, 0xb4, 0xfa, 0x58, 0xcf, - 0xc7, 0xab, 0xf7, 0x7b, 0xfa, 0xf0, 0xb9, 0xe4, 0xfc, 0xc3, 0x1c, 0xe4, 0xaa, 0x13, 0x0b, 0x47, - 0x7a, 0x73, 0xa0, 0x7a, 0xd3, 0xfd, 0xce, 0x5a, 0x0d, 0xae, 0xa1, 0xfa, 0x43, 0x2f, 0xd1, 0xd8, - 0x8d, 0xd3, 0x37, 0x73, 0x30, 0x52, 0x9d, 0x5a, 0x5c, 0x90, 0x4e, 0x82, 0x6f, 0x72, 0x4f, 0x3e, - 0xf4, 0x29, 0xe3, 0x5d, 0x7a, 0x2e, 0x61, 0xcf, 0xdc, 0xaa, 0x58, 0xde, 0xb3, 0xd7, 0x6f, 0xeb, - 0x8d, 0x36, 0xc5, 0xa3, 0x17, 0xee, 0xf7, 0xeb, 0x9a, 0x6f, 0xd1, 0x2f, 0xe1, 0xc3, 0x7f, 0x9f, - 0x01, 0x79, 0x11, 0x72, 0xb7, 0x84, 0x47, 0x46, 0x27, 0x3e, 0x4f, 0x5f, 0xe3, 0x7c, 0xd8, 0x24, - 0x98, 0x6b, 0x9b, 0x06, 0x72, 0x60, 0xa5, 0x58, 0xe1, 0x1b, 0x62, 0x01, 0xde, 0x56, 0xe1, 0x15, - 0xbf, 0xf0, 0x8d, 0x4a, 0x99, 0x54, 0x61, 0x70, 0x81, 0x3a, 0x4d, 0x13, 0x3b, 0xca, 0x9f, 0xb3, - 0xbb, 0x33, 0x61, 0x3b, 0x95, 0xc1, 0x56, 0x58, 0x08, 0x99, 0xc9, 0x5c, 0xc8, 0x1b, 0x00, 0xdc, - 0x46, 0xd9, 0x66, 0x20, 0xc7, 0xf3, 0x68, 0xf7, 0x73, 0xd3, 0x32, 0xc5, 0xc6, 0x93, 0x98, 0x91, - 0xbb, 0x30, 0x3a, 0x6b, 0x1b, 0xe6, 0xb2, 0xc9, 0x5d, 0x2f, 0xb1, 0x82, 0xc2, 0xd6, 0x0e, 0x4f, - 0xcc, 0x94, 0x6c, 0x4a, 0xe5, 0xd2, 0xaa, 0x49, 0x30, 0x56, 0xff, 0x5e, 0x2f, 0xe4, 0x59, 0xb7, - 0x1f, 0x8d, 0xdf, 0xbd, 0x8c, 0xdf, 0x12, 0x8c, 0xde, 0xb1, 0x9d, 0xbb, 0xa6, 0xb5, 0x12, 0x78, - 0xc5, 0x8b, 0xbd, 0x29, 0x7a, 0xf2, 0xac, 0x71, 0x5c, 0x2d, 0x70, 0xa0, 0xd7, 0x12, 0xe4, 0x5b, - 0x8c, 0xe0, 0xe7, 0x01, 0xf8, 0x5b, 0x77, 0xa4, 0xe9, 0x0f, 0x83, 0x55, 0xf0, 0x97, 0xf0, 0xe8, - 0x68, 0x2f, 0x07, 0xab, 0x08, 0x89, 0xd9, 0x26, 0x9c, 0xfb, 0x42, 0x0c, 0xa0, 0xdf, 0x3d, 0x6e, - 0xc2, 0xd1, 0x17, 0x42, 0x36, 0x02, 0xb8, 0x57, 0xc4, 0x02, 0x80, 0x74, 0xbf, 0x04, 0x31, 0x41, - 0x44, 0x26, 0x07, 0x11, 0x1e, 0x2e, 0xe5, 0x7a, 0x49, 0x93, 0x78, 0x90, 0x67, 0x63, 0x17, 0xe0, - 0x24, 0xc2, 0xad, 0xe3, 0xfd, 0x77, 0xe8, 0x40, 0x35, 0xb4, 0x95, 0x03, 0x95, 0xfa, 0x99, 0x2c, - 0x0c, 0x54, 0xdb, 0x4b, 0xee, 0xba, 0xeb, 0xd1, 0xe6, 0x21, 0x57, 0x63, 0x7f, 0x7b, 0x95, 0x4f, - 0xdd, 0x5e, 0x3d, 0xea, 0x0b, 0x45, 0x3a, 0x77, 0x0c, 0x4c, 0x3a, 0x5f, 0x1c, 0x7f, 0x33, 0x0b, - 0xa3, 0xfc, 0xe2, 0xac, 0x6c, 0xba, 0xf5, 0x7d, 0x70, 0xe6, 0x3f, 0x78, 0xa9, 0xec, 0xed, 0xb2, - 0x79, 0x1b, 0x4f, 0x24, 0xd4, 0x4f, 0x66, 0x61, 0xb0, 0xd4, 0xf6, 0x56, 0x4b, 0x1e, 0xea, 0xd6, - 0x03, 0xb9, 0x3f, 0xf9, 0x8d, 0x0c, 0x1c, 0x63, 0x0d, 0x59, 0xb4, 0xef, 0x52, 0x6b, 0x1f, 0x0e, - 0x1e, 0xe5, 0x03, 0xc4, 0xec, 0x2e, 0x0f, 0x10, 0x7d, 0x59, 0xe6, 0x76, 0x26, 0x4b, 0x3c, 0x2e, - 0xd7, 0xec, 0x06, 0x3d, 0xdc, 0x9f, 0xb1, 0x8f, 0xc7, 0xe5, 0xbe, 0x40, 0xf6, 0xe1, 0x7a, 0xe6, - 0x5b, 0x4b, 0x20, 0xfb, 0x70, 0xb6, 0xf4, 0xad, 0x21, 0x90, 0xaf, 0x66, 0x60, 0x60, 0xdc, 0xf6, - 0x0e, 0xf9, 0xc0, 0x17, 0x5f, 0x71, 0xb8, 0xd5, 0xdc, 0xff, 0x8a, 0xc3, 0xad, 0x9b, 0xea, 0x0f, - 0x65, 0xe1, 0x84, 0x08, 0xd2, 0x2d, 0xce, 0x1f, 0x8e, 0xa6, 0x63, 0x31, 0xd8, 0x92, 0xa2, 0x39, - 0x9a, 0x87, 0x84, 0x68, 0x7e, 0x26, 0x07, 0x27, 0x30, 0x94, 0x29, 0xdb, 0x96, 0x7d, 0x0b, 0xd8, - 0x22, 0xa4, 0x1e, 0xbd, 0x04, 0x9d, 0x4d, 0xb9, 0x04, 0xfd, 0xd3, 0x8d, 0xe2, 0xb3, 0x2b, 0xa6, - 0xb7, 0xda, 0x5e, 0xba, 0x52, 0xb7, 0x9b, 0x57, 0x57, 0x1c, 0xfd, 0x9e, 0xc9, 0xaf, 0xff, 0xf4, - 0xc6, 0xd5, 0x20, 0xdf, 0x85, 0xde, 0x32, 0x45, 0x26, 0x8c, 0x2a, 0xee, 0x75, 0x18, 0x57, 0xff, - 0xfa, 0xd4, 0x05, 0x78, 0xcd, 0x36, 0x2d, 0xe1, 0x53, 0xc8, 0x0d, 0xdd, 0x2a, 0xdb, 0x1f, 0xbe, - 0x69, 0x9b, 0x56, 0x2d, 0xee, 0x58, 0xb8, 0xd3, 0xfa, 0x42, 0xd6, 0x9a, 0x54, 0x8d, 0xfa, 0xcf, - 0x32, 0x70, 0x26, 0xaa, 0xc5, 0xdf, 0x0a, 0xb6, 0xe3, 0x0f, 0x67, 0xe1, 0xe4, 0x0d, 0x14, 0x4e, - 0xe0, 0xc8, 0x71, 0x34, 0x6f, 0x89, 0xc1, 0x99, 0x22, 0x9b, 0x23, 0x8b, 0xb2, 0xb3, 0x6c, 0x8e, - 0x26, 0x75, 0x21, 0x9b, 0xdf, 0xcc, 0xc0, 0xf1, 0xf9, 0x4a, 0x79, 0xe2, 0x5b, 0x64, 0x44, 0x25, - 0xbf, 0xe7, 0x90, 0x1b, 0x9c, 0x89, 0xef, 0x39, 0xe4, 0xa6, 0xe7, 0x17, 0xb2, 0x70, 0xbc, 0x5a, - 0x9a, 0x9d, 0xf9, 0x56, 0x99, 0xc1, 0x27, 0x64, 0xaf, 0x43, 0xff, 0x10, 0x4c, 0xd8, 0x02, 0xf2, - 0x67, 0xde, 0xbe, 0xd6, 0xd9, 0x1b, 0x31, 0x29, 0x94, 0x43, 0x3e, 0x75, 0xef, 0x8b, 0x50, 0x98, - 0xe6, 0x47, 0xa8, 0x0f, 0xb9, 0xe6, 0xff, 0x83, 0x02, 0x0c, 0xde, 0x6c, 0x2f, 0x51, 0xe1, 0x9c, - 0xf2, 0x40, 0x9f, 0xfc, 0x5e, 0x83, 0x41, 0x21, 0x06, 0xbc, 0x35, 0x91, 0x82, 0xe7, 0x89, 0x60, - 0x28, 0x3c, 0x3e, 0x91, 0x4c, 0x44, 0xce, 0x41, 0xfe, 0x36, 0x75, 0x96, 0xe4, 0x77, 0xa5, 0xf7, - 0xa8, 0xb3, 0xa4, 0x21, 0x94, 0xcc, 0x84, 0x2e, 0xf3, 0xa5, 0x85, 0x0a, 0x26, 0x52, 0x11, 0x17, - 0x36, 0x98, 0x19, 0x26, 0xf0, 0x7b, 0xd3, 0x5b, 0x26, 0x4f, 0xc1, 0x22, 0xbf, 0x69, 0x8f, 0x97, - 0x24, 0x73, 0x30, 0x26, 0x3b, 0x3e, 0xf1, 0x2c, 0x22, 0xfd, 0x29, 0xec, 0xd2, 0xf2, 0x87, 0x24, - 0x8b, 0x92, 0x57, 0x60, 0xc8, 0x07, 0xa2, 0x0b, 0xd7, 0x40, 0x18, 0xba, 0x3e, 0x60, 0x15, 0x4b, - 0x51, 0x14, 0x29, 0x20, 0x33, 0xc0, 0x6b, 0x08, 0x48, 0x61, 0x10, 0x73, 0x89, 0x8b, 0x14, 0x20, - 0xcf, 0x20, 0x03, 0x7c, 0xe6, 0x81, 0xce, 0x2a, 0x83, 0xf8, 0xe8, 0x12, 0x5d, 0xf2, 0x1d, 0x01, - 0xe7, 0x4f, 0x6b, 0x23, 0x64, 0x64, 0x1e, 0x20, 0x74, 0x2a, 0x10, 0x01, 0x0c, 0x76, 0xec, 0xee, - 0x20, 0xb1, 0x90, 0xaf, 0x03, 0x87, 0x77, 0x73, 0x1d, 0xa8, 0xfe, 0x76, 0x16, 0x06, 0x4b, 0xad, - 0x56, 0x30, 0x14, 0x9e, 0x84, 0x42, 0xa9, 0xd5, 0xba, 0xa5, 0x55, 0xe4, 0x50, 0xe6, 0x7a, 0xab, - 0x55, 0x6b, 0x3b, 0xa6, 0xec, 0x13, 0xca, 0x89, 0xc8, 0x04, 0x0c, 0x97, 0x5a, 0xad, 0x85, 0xf6, - 0x52, 0xc3, 0xac, 0x4b, 0x99, 0x91, 0x78, 0x12, 0xb7, 0x56, 0xab, 0xd6, 0x42, 0x4c, 0x3c, 0x3d, - 0x56, 0xb4, 0x0c, 0xf9, 0x38, 0x86, 0xfd, 0x11, 0x89, 0x79, 0x78, 0xea, 0x0f, 0x35, 0x08, 0x62, - 0x1e, 0xb6, 0xed, 0x4a, 0x40, 0xc4, 0x83, 0xbd, 0x9f, 0xf3, 0x43, 0xe6, 0xb3, 0x8a, 0x12, 0x09, - 0x78, 0x42, 0x96, 0xe4, 0xfd, 0xd0, 0x57, 0x6a, 0xb5, 0xa4, 0xfb, 0x26, 0x74, 0x2a, 0x62, 0xa5, - 0x62, 0x7d, 0xec, 0x93, 0x9d, 0x7d, 0x09, 0x46, 0xa2, 0x95, 0xed, 0x28, 0x58, 0xfc, 0x9f, 0x64, - 0xf0, 0x83, 0x0e, 0xb9, 0x4f, 0xf3, 0xd3, 0x90, 0x2b, 0xb5, 0x5a, 0x62, 0x3e, 0x3a, 0x9e, 0xd2, - 0x1f, 0xf1, 0x27, 0xd0, 0xa5, 0x56, 0xcb, 0xff, 0xf4, 0x43, 0xfe, 0x38, 0x62, 0x57, 0x9f, 0xfe, - 0x55, 0xfe, 0xe9, 0x87, 0xfb, 0xe1, 0x82, 0xfa, 0x4b, 0x39, 0x38, 0x56, 0x6a, 0xb5, 0x8e, 0x82, - 0xcc, 0xef, 0xd7, 0x43, 0xeb, 0xa7, 0x00, 0xa4, 0xe9, 0xb1, 0x2f, 0x78, 0xba, 0x35, 0x28, 0x4d, - 0x8d, 0x4a, 0x46, 0x93, 0x88, 0x7c, 0xf5, 0xeb, 0xdf, 0x91, 0xfa, 0x7d, 0x32, 0x87, 0x53, 0xf1, - 0x61, 0x0f, 0x1a, 0xf5, 0x6e, 0xe9, 0x36, 0xd1, 0x07, 0x85, 0x1d, 0xf5, 0xc1, 0xaf, 0x47, 0x06, - 0x0f, 0x06, 0x2d, 0x3f, 0xea, 0x85, 0xde, 0x3d, 0x99, 0xc5, 0x23, 0xb2, 0x30, 0x45, 0x24, 0x1b, - 0x3f, 0x91, 0x92, 0x88, 0xab, 0x54, 0x67, 0xa8, 0x9a, 0x69, 0x68, 0x31, 0x5a, 0xbf, 0x0f, 0xfb, - 0x76, 0xd4, 0x87, 0x1b, 0x59, 0x7c, 0x3b, 0x1d, 0xc4, 0x65, 0xda, 0xfb, 0xee, 0xe2, 0x2a, 0x00, - 0xf7, 0x3c, 0x08, 0xdc, 0x9a, 0x87, 0x79, 0x08, 0x16, 0x9e, 0x5f, 0x49, 0x84, 0x60, 0x09, 0x49, - 0x02, 0x0f, 0xa9, 0x5c, 0xaa, 0x87, 0xd4, 0x65, 0xe8, 0xd7, 0xf4, 0xb5, 0xd7, 0xdb, 0xd4, 0x59, - 0x17, 0xe6, 0x0c, 0x0f, 0x7b, 0xa8, 0xaf, 0xd5, 0x3e, 0xc1, 0x80, 0x5a, 0x80, 0x26, 0x6a, 0xf0, - 0xf8, 0x5e, 0xf2, 0x08, 0xe1, 0x67, 0xe4, 0xc1, 0x93, 0xfb, 0xdd, 0x28, 0x3a, 0x79, 0x01, 0x72, - 0xa5, 0x3b, 0x55, 0x21, 0xd9, 0xa0, 0x6b, 0x4b, 0x77, 0xaa, 0x42, 0x5e, 0x1d, 0xcb, 0xde, 0xa9, - 0xaa, 0x9f, 0xcc, 0x02, 0x49, 0x52, 0x92, 0x67, 0x61, 0x00, 0xa1, 0x2b, 0x4c, 0x67, 0xe4, 0xc4, - 0x9c, 0x6b, 0x6e, 0xcd, 0x41, 0x68, 0xc4, 0xb8, 0xf3, 0x49, 0xc9, 0xf3, 0x98, 0x83, 0x58, 0xa4, - 0x86, 0x8b, 0x24, 0xe6, 0x5c, 0x73, 0xfd, 0xac, 0xbd, 0xb1, 0x14, 0xc4, 0x82, 0x18, 0xed, 0xc2, - 0x3b, 0xd5, 0x69, 0xdb, 0xf5, 0x84, 0xa8, 0xb9, 0x5d, 0xb8, 0xe6, 0x62, 0x46, 0xd8, 0x88, 0x5d, - 0xc8, 0xc9, 0x30, 0xab, 0xd5, 0x9d, 0x2a, 0x7f, 0xa6, 0x62, 0x68, 0x76, 0xc3, 0x37, 0x28, 0x79, - 0x56, 0xab, 0x35, 0xb7, 0xc6, 0x9f, 0xb8, 0x18, 0x98, 0xfc, 0x38, 0x92, 0xd5, 0x2a, 0x52, 0x4a, - 0xfd, 0x6c, 0x3f, 0x8c, 0x96, 0x75, 0x4f, 0x5f, 0xd2, 0x5d, 0x2a, 0xed, 0xa6, 0x8f, 0xf9, 0x30, - 0xff, 0x73, 0x24, 0x39, 0x18, 0x4b, 0x29, 0x5f, 0x13, 0x2f, 0x40, 0x5e, 0x0c, 0xf9, 0x06, 0x39, - 0x47, 0xe5, 0x24, 0x66, 0x4b, 0xb5, 0x96, 0x00, 0x6b, 0x09, 0x42, 0xf2, 0x04, 0x0c, 0xfa, 0x30, - 0xb6, 0x01, 0xc8, 0x85, 0x3a, 0x63, 0x2c, 0x31, 0xfb, 0x5f, 0x93, 0xd1, 0xe4, 0x79, 0x18, 0xf2, - 0x7f, 0x4a, 0xa6, 0x35, 0xcf, 0xc8, 0xb6, 0x94, 0xd8, 0x3d, 0xc9, 0xa4, 0x72, 0x51, 0x9c, 0xdf, - 0x7a, 0x23, 0x45, 0x63, 0x49, 0xcf, 0x22, 0xa4, 0xe4, 0x13, 0x30, 0xe2, 0xff, 0x16, 0x1b, 0x06, - 0x9e, 0x1f, 0xee, 0x89, 0x20, 0xb7, 0x72, 0x4c, 0xac, 0x57, 0xa2, 0xe4, 0x7c, 0xeb, 0xf0, 0x90, - 0x9f, 0xc7, 0xcb, 0x58, 0x4a, 0xee, 0x1c, 0x62, 0x15, 0x90, 0x0a, 0x8c, 0xf9, 0x90, 0x50, 0x43, - 0xfb, 0xc2, 0x1d, 0xa3, 0xb1, 0x54, 0x4b, 0x55, 0xd2, 0x64, 0x29, 0xd2, 0x80, 0x73, 0x11, 0xa0, - 0xe1, 0xae, 0x9a, 0xcb, 0x9e, 0xd8, 0xee, 0x89, 0x18, 0xc4, 0x22, 0x71, 0x63, 0xc0, 0x95, 0xd3, - 0xf8, 0x19, 0x58, 0xa3, 0xd9, 0xa1, 0xba, 0x72, 0x23, 0x55, 0x38, 0xe1, 0xe3, 0x6f, 0x4c, 0x2c, - 0x2c, 0x38, 0xf6, 0x9b, 0xb4, 0xee, 0x55, 0xca, 0x62, 0xbb, 0x8c, 0xb1, 0xe9, 0x8c, 0xa5, 0xda, - 0x4a, 0xbd, 0xc5, 0x94, 0x82, 0xe1, 0xa2, 0xcc, 0x53, 0x0b, 0x93, 0xdb, 0x70, 0x52, 0x82, 0x57, - 0x2c, 0xd7, 0xd3, 0xad, 0x3a, 0xad, 0x94, 0xc5, 0x1e, 0x1a, 0xf7, 0xf3, 0x82, 0xab, 0x29, 0x90, - 0x51, 0xb6, 0xe9, 0xc5, 0xc9, 0x4b, 0x30, 0xec, 0x23, 0xf8, 0x2d, 0xe2, 0x20, 0xde, 0x22, 0xe2, - 0x90, 0x34, 0x96, 0x6a, 0xf1, 0xd7, 0x94, 0x51, 0x62, 0x59, 0xa3, 0x30, 0xb5, 0xfd, 0x50, 0x44, - 0xa3, 0xbc, 0xf5, 0x56, 0xaa, 0x32, 0x62, 0xba, 0xfb, 0x57, 0x42, 0x8d, 0x9a, 0x77, 0xcc, 0x15, - 0x93, 0xef, 0xa4, 0xfd, 0x07, 0x94, 0x4b, 0x35, 0x1b, 0x81, 0x69, 0xfa, 0xc1, 0xc9, 0xcf, 0x96, - 0xe0, 0x78, 0x8a, 0x8e, 0xed, 0x68, 0xc7, 0xf8, 0x99, 0x6c, 0xd8, 0x88, 0x43, 0xbe, 0x6d, 0x1c, - 0x87, 0x7e, 0xff, 0x4b, 0x84, 0xf1, 0xa0, 0x74, 0x1a, 0x9a, 0x71, 0x1e, 0x3e, 0x3e, 0x22, 0x8e, - 0x43, 0xbe, 0x95, 0xdc, 0x0f, 0x71, 0xbc, 0x9d, 0x09, 0xc5, 0x71, 0xc8, 0xb7, 0x97, 0xbf, 0x99, - 0x0b, 0xe7, 0xa4, 0xa3, 0x3d, 0xe6, 0x7e, 0x99, 0xc9, 0xa1, 0x1f, 0x6c, 0x61, 0x07, 0x0f, 0x19, - 0x65, 0xd5, 0xec, 0xdb, 0xa5, 0x6a, 0xfe, 0x6e, 0xb2, 0x3f, 0xb9, 0xe9, 0x79, 0x28, 0xfb, 0x73, - 0x1f, 0x06, 0x2b, 0xb9, 0x16, 0xae, 0x63, 0xdc, 0x46, 0xef, 0x95, 0x42, 0xfc, 0x2d, 0x09, 0x13, - 0x3d, 0x4a, 0x42, 0x3e, 0x02, 0xa7, 0x23, 0x80, 0x05, 0xdd, 0xd1, 0x9b, 0xd4, 0x0b, 0x33, 0x0e, - 0x62, 0xd0, 0x26, 0xbf, 0x74, 0xad, 0x15, 0xa0, 0xe5, 0x2c, 0x86, 0x1d, 0x38, 0x48, 0xca, 0xd1, - 0xb7, 0x03, 0x27, 0xe9, 0x2f, 0xe6, 0x42, 0x53, 0x25, 0x1a, 0x7c, 0x55, 0xa3, 0x6e, 0xbb, 0xe1, - 0x3d, 0xb8, 0x1d, 0xbc, 0xbb, 0xd4, 0x16, 0xd3, 0x70, 0xac, 0xb4, 0xbc, 0x4c, 0xeb, 0x9e, 0x1f, - 0x53, 0xda, 0x15, 0xe1, 0xf6, 0xf8, 0xd6, 0x41, 0xa0, 0x44, 0x8c, 0xe0, 0x48, 0x6e, 0xfc, 0x58, - 0x31, 0xf5, 0xf7, 0xf2, 0xa0, 0x04, 0xa6, 0x7b, 0xf0, 0x50, 0xeb, 0x00, 0x97, 0xc9, 0x77, 0x45, - 0xaf, 0x98, 0x30, 0x16, 0x0a, 0xa3, 0xda, 0x6e, 0x36, 0x75, 0x1c, 0x7a, 0x6c, 0x6b, 0x50, 0x8c, - 0x33, 0x0b, 0x09, 0xf9, 0x6e, 0xe0, 0xac, 0xd8, 0x0d, 0x90, 0xf0, 0x21, 0x5c, 0xcd, 0xe5, 0x2c, - 0xb4, 0x24, 0x57, 0xf2, 0xb9, 0x0c, 0x9c, 0xf0, 0x3b, 0x65, 0x7e, 0x89, 0x99, 0xc5, 0x13, 0x76, - 0xdb, 0xf2, 0xfc, 0x9d, 0xc8, 0x0b, 0x9d, 0xab, 0xe3, 0x9d, 0x74, 0x25, 0xad, 0x30, 0x6f, 0x49, - 0x10, 0x58, 0x22, 0x50, 0x08, 0x1b, 0x69, 0x6a, 0x75, 0x24, 0xd2, 0x52, 0xeb, 0x3d, 0x7b, 0x03, - 0xce, 0x74, 0x64, 0xb9, 0x95, 0x19, 0xda, 0x2b, 0x9b, 0xa1, 0xff, 0x22, 0x13, 0x4e, 0x44, 0x31, - 0x21, 0x91, 0x2b, 0x00, 0x21, 0x48, 0x6c, 0x4c, 0x47, 0x36, 0x37, 0x8a, 0x10, 0x0a, 0x4d, 0x93, - 0x28, 0xc8, 0x3c, 0x14, 0x84, 0x58, 0x78, 0x76, 0xdf, 0xf7, 0x6d, 0xd1, 0x0b, 0x57, 0x64, 0x39, - 0xe0, 0xa6, 0x53, 0x7c, 0xb3, 0x60, 0x73, 0xf6, 0x79, 0x18, 0xdc, 0xed, 0x77, 0x7d, 0x2e, 0x07, - 0x44, 0xde, 0x45, 0x1e, 0xa0, 0x89, 0x7d, 0x88, 0xa7, 0xb0, 0x4b, 0xd0, 0xcf, 0x3e, 0x01, 0xf3, - 0x5d, 0x48, 0xf1, 0x6d, 0xdb, 0x02, 0xa6, 0x05, 0xd8, 0x30, 0xb8, 0x54, 0x5f, 0x7a, 0x70, 0x29, - 0xf5, 0x07, 0x72, 0x70, 0x4a, 0xee, 0x90, 0x32, 0xc5, 0x90, 0xf9, 0x47, 0x9d, 0xf2, 0x0e, 0x76, - 0x8a, 0x0a, 0x05, 0xbe, 0x79, 0x10, 0xb9, 0x0b, 0xf8, 0xc1, 0x0e, 0x42, 0x34, 0x81, 0x51, 0xff, - 0x6d, 0x16, 0x86, 0x17, 0x6c, 0xd7, 0x5b, 0x71, 0xa8, 0xbb, 0xa0, 0x3b, 0xee, 0x03, 0xdc, 0x1d, - 0x1f, 0x80, 0x61, 0x0c, 0x0f, 0xd4, 0xa4, 0x16, 0x0f, 0xa1, 0xd3, 0x2b, 0x25, 0x1b, 0xf1, 0x11, - 0x22, 0xaf, 0x54, 0x84, 0x90, 0x69, 0x3f, 0xb7, 0xfc, 0xa4, 0xa0, 0x4d, 0xdc, 0xec, 0xe3, 0x70, - 0xf5, 0x27, 0x72, 0x30, 0xe4, 0x4b, 0x79, 0xdc, 0x3c, 0xac, 0x37, 0x35, 0x07, 0x2b, 0xe4, 0xab, - 0x00, 0x0b, 0xb6, 0xe3, 0xe9, 0x8d, 0xb9, 0x50, 0xf3, 0xf1, 0x88, 0xb3, 0x85, 0x50, 0x5e, 0x46, - 0x22, 0xc1, 0xf5, 0x2b, 0x34, 0xab, 0xf9, 0xc4, 0xc4, 0xd7, 0xaf, 0x00, 0xaa, 0x49, 0x14, 0xea, - 0xaf, 0x66, 0xe1, 0x98, 0xdf, 0x49, 0x93, 0xf7, 0x69, 0xbd, 0xfd, 0x20, 0xcf, 0x4d, 0x51, 0x69, - 0xf7, 0x6e, 0x29, 0x6d, 0xf5, 0xbf, 0x4a, 0x13, 0xc9, 0x44, 0xc3, 0x3e, 0x9a, 0x48, 0xfe, 0x3c, - 0x74, 0x5c, 0xfd, 0xce, 0x1c, 0x9c, 0xf0, 0xa5, 0x3e, 0xd5, 0xb6, 0xf0, 0x70, 0x60, 0x42, 0x6f, - 0x34, 0x1e, 0xe4, 0xdd, 0xf8, 0xa0, 0x2f, 0x88, 0x79, 0x11, 0x6f, 0x4f, 0xe4, 0xf8, 0x5b, 0x16, - 0xe0, 0x9a, 0x6d, 0x1a, 0x9a, 0x4c, 0x44, 0x5e, 0x81, 0x21, 0xff, 0x67, 0xc9, 0x59, 0xf1, 0xb7, - 0xe0, 0x78, 0xd4, 0x1f, 0x14, 0xd2, 0x9d, 0x48, 0x58, 0x81, 0x48, 0x01, 0xf5, 0xdf, 0x17, 0xe0, - 0xec, 0x1d, 0xd3, 0x32, 0xec, 0x35, 0xd7, 0x4f, 0x11, 0x79, 0xe8, 0x8f, 0xba, 0x0e, 0x3a, 0x35, - 0xe4, 0xeb, 0x70, 0x32, 0x2e, 0x52, 0x27, 0x08, 0xdc, 0x2d, 0x7a, 0x67, 0x8d, 0x13, 0xd4, 0xfc, - 0x64, 0x91, 0xe2, 0xbe, 0x4c, 0x4b, 0x2f, 0x19, 0xcf, 0x36, 0xd9, 0xb7, 0x9d, 0x6c, 0x93, 0x8f, - 0x43, 0xa1, 0x6c, 0x37, 0x75, 0xd3, 0x0f, 0x30, 0x83, 0xa3, 0x38, 0xa8, 0x17, 0x31, 0x9a, 0xa0, - 0x60, 0xfc, 0x45, 0xc5, 0xd8, 0x65, 0x03, 0x21, 0x7f, 0xbf, 0x00, 0xb3, 0xd2, 0x34, 0x99, 0x88, - 0xd8, 0x30, 0x2c, 0xaa, 0x13, 0xb7, 0x5b, 0x80, 0x9b, 0xa7, 0x67, 0x7c, 0x19, 0x75, 0x56, 0xab, - 0x2b, 0x91, 0x72, 0x7c, 0x1b, 0xc5, 0x93, 0x60, 0x8a, 0x8f, 0xe1, 0xf7, 0x5c, 0x5a, 0x94, 0xbf, - 0x24, 0x04, 0x9c, 0x64, 0x06, 0x93, 0x42, 0xc0, 0x59, 0x46, 0x26, 0x22, 0x93, 0x30, 0x86, 0xe1, - 0x95, 0x83, 0xad, 0x14, 0x53, 0x89, 0x21, 0x34, 0x2a, 0xf1, 0xd2, 0x84, 0x47, 0x64, 0x66, 0x1f, - 0x57, 0xab, 0x0b, 0xb4, 0x96, 0x2c, 0x71, 0xf6, 0x55, 0x20, 0xc9, 0x36, 0xef, 0xe8, 0xda, 0xe4, - 0x1f, 0x49, 0xfb, 0xba, 0xc3, 0xee, 0xf8, 0xb2, 0x1f, 0xb3, 0x5d, 0x24, 0x75, 0x58, 0xef, 0x3b, - 0x99, 0x3a, 0xac, 0xb0, 0xaf, 0xa9, 0xc3, 0xd4, 0x9f, 0xcf, 0xc0, 0x58, 0x22, 0xce, 0x38, 0x79, - 0x1a, 0x80, 0x43, 0xa4, 0x78, 0x8e, 0x18, 0x20, 0x25, 0x8c, 0x3d, 0x2e, 0xd6, 0xc0, 0x90, 0x8c, - 0x5c, 0x85, 0x7e, 0xfe, 0x4b, 0xc4, 0x60, 0x4a, 0x16, 0x69, 0xb7, 0x4d, 0x43, 0x0b, 0x88, 0xc2, - 0x5a, 0xf0, 0xe2, 0x30, 0x97, 0x5a, 0xc4, 0x5b, 0x6f, 0x05, 0xb5, 0x30, 0x32, 0xf5, 0xb3, 0x59, - 0x18, 0x0a, 0x1a, 0x5c, 0x32, 0x0e, 0x4a, 0xe7, 0x0a, 0x22, 0x64, 0x7b, 0x6e, 0xab, 0x90, 0xed, - 0xb1, 0x49, 0x55, 0xc4, 0x68, 0xdf, 0xbf, 0x77, 0x4f, 0x9f, 0xcf, 0xc2, 0xb1, 0xa0, 0xd6, 0x03, - 0xbc, 0xa3, 0x7a, 0x17, 0x89, 0xe4, 0x73, 0x19, 0x50, 0xc6, 0xcd, 0x46, 0xc3, 0xb4, 0x56, 0x2a, - 0xd6, 0xb2, 0xed, 0x34, 0x71, 0xd6, 0x3b, 0xb8, 0x73, 0x5a, 0xf5, 0x7b, 0x32, 0x30, 0x26, 0x1a, - 0x34, 0xa1, 0x3b, 0xc6, 0xc1, 0x1d, 0x82, 0xc5, 0x5b, 0x72, 0x70, 0xfa, 0xa2, 0x7e, 0x39, 0x0b, - 0x30, 0x63, 0xd7, 0xef, 0x1e, 0xf2, 0x67, 0x53, 0x2f, 0x42, 0x81, 0x07, 0xc2, 0x12, 0x1a, 0x3b, - 0x26, 0x9e, 0x07, 0xb1, 0x4f, 0xe3, 0x88, 0xf1, 0x51, 0x31, 0x1f, 0x17, 0x78, 0x20, 0x2d, 0x25, - 0xa3, 0x89, 0x22, 0xac, 0x52, 0x46, 0x27, 0x16, 0x8c, 0xa0, 0x52, 0x06, 0x8b, 0x56, 0xba, 0xb9, - 0x51, 0xcc, 0x37, 0xec, 0xfa, 0x5d, 0x0d, 0xe9, 0xd5, 0x3f, 0xcb, 0x70, 0xd9, 0x1d, 0xf2, 0xc7, - 0x9f, 0xfe, 0xe7, 0xe7, 0x77, 0xf8, 0xf9, 0xdf, 0x9b, 0x81, 0x13, 0x1a, 0xad, 0xdb, 0xf7, 0xa8, - 0xb3, 0x3e, 0x61, 0x1b, 0xf4, 0x06, 0xb5, 0xa8, 0x73, 0x50, 0x23, 0xea, 0xef, 0x62, 0x8e, 0x8b, - 0xb0, 0x31, 0xb7, 0x5c, 0x6a, 0x1c, 0x9e, 0xfc, 0x23, 0xea, 0xdf, 0xee, 0x03, 0x25, 0xd5, 0xb4, - 0x3d, 0xb4, 0xe6, 0x5c, 0xc7, 0xfd, 0x4a, 0x7e, 0xbf, 0xf6, 0x2b, 0xbd, 0x3b, 0xdb, 0xaf, 0x14, - 0x76, 0xba, 0x5f, 0xe9, 0xdb, 0xce, 0x7e, 0xa5, 0x19, 0xdf, 0xaf, 0xf4, 0xe3, 0x7e, 0xe5, 0xe9, - 0xae, 0xfb, 0x95, 0x49, 0xcb, 0xd8, 0xe5, 0x6e, 0xe5, 0xd0, 0xe6, 0xc6, 0xdd, 0xcd, 0x36, 0xeb, - 0x12, 0x9b, 0x14, 0xeb, 0xb6, 0x63, 0x50, 0x43, 0xec, 0xae, 0xf0, 0x68, 0xdf, 0x11, 0x30, 0x2d, - 0xc0, 0x26, 0x12, 0x0d, 0x0f, 0x6f, 0x27, 0xd1, 0xf0, 0x3e, 0xec, 0xbf, 0x3e, 0x93, 0x85, 0xb1, - 0x09, 0xea, 0x78, 0x3c, 0xd2, 0xe6, 0x7e, 0x78, 0xae, 0x95, 0xe0, 0x98, 0xc4, 0x10, 0x2d, 0xf2, - 0x6c, 0xe8, 0x8d, 0x57, 0xa7, 0x8e, 0x17, 0x77, 0xe6, 0x8b, 0xd3, 0xb3, 0xea, 0xfd, 0x64, 0x5f, - 0x62, 0xec, 0x06, 0xd5, 0xfb, 0x70, 0x2e, 0x48, 0x53, 0xfc, 0xd2, 0x02, 0x7a, 0x29, 0x7f, 0x57, - 0x7e, 0xe7, 0xf9, 0xbb, 0xd4, 0x9f, 0xcb, 0xc0, 0x45, 0x8d, 0x5a, 0x74, 0x4d, 0x5f, 0x6a, 0x50, - 0xa9, 0x59, 0x62, 0x65, 0x60, 0xb3, 0x86, 0xe9, 0x36, 0x75, 0xaf, 0xbe, 0xba, 0x27, 0x19, 0x4d, - 0xc1, 0x90, 0x3c, 0x7f, 0xed, 0x60, 0x6e, 0x8b, 0x94, 0x53, 0xff, 0x53, 0x0e, 0xfa, 0xc6, 0x6d, - 0xef, 0x35, 0x7b, 0x8f, 0x09, 0xe5, 0xc2, 0x29, 0x3f, 0xbb, 0x83, 0x03, 0x9d, 0xf7, 0x63, 0xe5, - 0x52, 0x8c, 0x7d, 0xf4, 0xf4, 0x5c, 0xb2, 0x13, 0xb9, 0x08, 0x7c, 0xb2, 0x1d, 0xa6, 0x92, 0x7b, - 0x16, 0x06, 0x30, 0x48, 0x8b, 0x74, 0xe4, 0x8a, 0x7e, 0xd4, 0x1e, 0x03, 0xc6, 0xeb, 0x08, 0x49, - 0xc9, 0x47, 0x22, 0xa1, 0x41, 0x0b, 0x7b, 0x4f, 0x3d, 0x27, 0x47, 0x09, 0x7d, 0x9a, 0xdf, 0xd6, - 0x61, 0x9b, 0xa4, 0x34, 0x1d, 0x78, 0x54, 0x12, 0x6b, 0x52, 0x40, 0xb8, 0x7f, 0x69, 0xe1, 0xd4, - 0x6f, 0xe6, 0x61, 0xc8, 0x77, 0xb9, 0x3d, 0xa0, 0x6e, 0x7f, 0x12, 0x0a, 0xd3, 0xb6, 0x94, 0x64, - 0x00, 0x5d, 0x74, 0x57, 0x6d, 0x37, 0xe6, 0x7b, 0x2c, 0x88, 0x98, 0xc0, 0xe6, 0x6c, 0x43, 0x76, - 0x30, 0x47, 0x81, 0x59, 0xb6, 0x91, 0x78, 0xa0, 0x1b, 0x10, 0x92, 0x8b, 0x90, 0x47, 0xdf, 0x7c, - 0xe9, 0xa0, 0x3d, 0xe6, 0x8f, 0x8f, 0x78, 0x49, 0xa1, 0x0a, 0x3b, 0x55, 0xa8, 0xbe, 0xdd, 0x2a, - 0x54, 0xff, 0xfe, 0x2a, 0xd4, 0x1b, 0x30, 0x84, 0x35, 0xf9, 0x39, 0xca, 0xb6, 0x5e, 0x13, 0xcf, - 0x88, 0x65, 0x6b, 0x98, 0xb7, 0x5b, 0x64, 0x2a, 0xc3, 0xd5, 0x2a, 0xc2, 0x2a, 0xa6, 0x76, 0xb0, - 0x07, 0xb5, 0xfb, 0xdd, 0x0c, 0xf4, 0xdd, 0xb2, 0xee, 0x5a, 0xf6, 0xda, 0xde, 0x34, 0xee, 0x69, - 0x18, 0x14, 0x6c, 0xa4, 0x85, 0x01, 0xdf, 0x5c, 0xb7, 0x39, 0xb8, 0x86, 0x9c, 0x34, 0x99, 0x8a, - 0xbc, 0x14, 0x14, 0xc2, 0xe7, 0x37, 0xb9, 0x30, 0x4d, 0x87, 0x5f, 0xa8, 0x1e, 0xcd, 0x2c, 0x20, - 0x93, 0x93, 0x73, 0x90, 0x2f, 0xb3, 0xa6, 0x4a, 0x71, 0x6a, 0x59, 0x53, 0x34, 0x84, 0xaa, 0x9f, - 0xc9, 0xc3, 0x48, 0xec, 0xcc, 0xea, 0x71, 0x18, 0x10, 0x67, 0x46, 0xa6, 0x9f, 0xea, 0x00, 0x9f, - 0xe7, 0x04, 0x40, 0xad, 0x9f, 0xff, 0x59, 0x31, 0xc8, 0x07, 0xa1, 0xcf, 0x76, 0x71, 0x3d, 0xc3, - 0x6f, 0x19, 0x09, 0x87, 0xd0, 0x7c, 0x95, 0xb5, 0x9d, 0x0f, 0x0e, 0x41, 0x22, 0x6b, 0xa4, 0xed, - 0xe2, 0xa7, 0x5d, 0x87, 0x01, 0xdd, 0x75, 0xa9, 0x57, 0xf3, 0xf4, 0x15, 0x39, 0xfb, 0x41, 0x00, - 0x94, 0x47, 0x07, 0x02, 0x17, 0xf5, 0x15, 0xf2, 0x2a, 0x0c, 0xd7, 0x1d, 0x8a, 0x2b, 0x9e, 0xde, - 0x60, 0xad, 0x94, 0x2c, 0xd2, 0x08, 0x42, 0xbe, 0xdf, 0x08, 0x11, 0x15, 0x83, 0xdc, 0x86, 0x61, - 0xf1, 0x39, 0xdc, 0x37, 0x1e, 0x07, 0xda, 0x48, 0xb8, 0x02, 0x71, 0x91, 0x70, 0xef, 0x78, 0xf1, - 0x44, 0x42, 0x26, 0x97, 0xf9, 0x1a, 0x12, 0x29, 0x99, 0x07, 0xb2, 0x46, 0x97, 0x6a, 0x7a, 0xdb, - 0x5b, 0x65, 0x75, 0xf1, 0xe0, 0xdd, 0x22, 0xe9, 0x1f, 0xbe, 0x2b, 0x48, 0x62, 0xe5, 0xe7, 0x16, - 0x6b, 0x74, 0xa9, 0x14, 0x41, 0x92, 0x3b, 0x70, 0x32, 0x59, 0x84, 0x7d, 0x32, 0x3f, 0xbc, 0x7f, - 0x74, 0x73, 0xa3, 0x58, 0x4c, 0x25, 0x90, 0xd8, 0x1e, 0x4f, 0xb0, 0xad, 0x18, 0xaf, 0xe5, 0xfb, - 0xfb, 0x46, 0xfb, 0xb5, 0x11, 0x56, 0xd6, 0xb7, 0xfe, 0x4c, 0x43, 0xfd, 0x5a, 0x86, 0x59, 0x79, - 0xec, 0x83, 0x30, 0xeb, 0x31, 0xd3, 0xf5, 0xe6, 0x0e, 0x75, 0xbd, 0x19, 0xe6, 0x27, 0x2c, 0xb8, - 0x5d, 0x66, 0x57, 0x4d, 0x60, 0xc9, 0x15, 0x28, 0x18, 0xf2, 0x81, 0xd7, 0xa9, 0x68, 0x27, 0xf8, - 0xf5, 0x68, 0x82, 0x8a, 0x5c, 0x82, 0x3c, 0x5b, 0x6d, 0xe2, 0xbb, 0x5d, 0xd9, 0x30, 0xd0, 0x90, - 0x42, 0xfd, 0xf6, 0x2c, 0x0c, 0x49, 0x5f, 0x73, 0x6d, 0x4f, 0x9f, 0xf3, 0xc2, 0xf6, 0x9a, 0xe9, - 0x3b, 0xa5, 0xe0, 0x36, 0xc8, 0x6f, 0xf2, 0xf5, 0x40, 0x14, 0xdb, 0xba, 0x30, 0x12, 0x82, 0x79, - 0x56, 0x7c, 0x68, 0x61, 0xfb, 0x3b, 0x3f, 0x46, 0xff, 0x5a, 0xbe, 0x3f, 0x3b, 0x9a, 0x7b, 0x2d, - 0xdf, 0x9f, 0x1f, 0xed, 0xc5, 0x48, 0x57, 0x18, 0x5c, 0x9a, 0x6f, 0xab, 0xad, 0x65, 0x73, 0xe5, - 0x90, 0xbf, 0xce, 0xd8, 0xdf, 0x28, 0x60, 0x31, 0xd9, 0x1c, 0xf2, 0xa7, 0x1a, 0xef, 0xa8, 0x6c, - 0x8e, 0xf2, 0x19, 0x0a, 0xd9, 0xfc, 0x5e, 0x06, 0x94, 0x54, 0xd9, 0x94, 0x0e, 0xc8, 0x4f, 0x61, - 0xff, 0xb2, 0x1a, 0x7e, 0x23, 0x0b, 0x63, 0x15, 0xcb, 0xa3, 0x2b, 0x7c, 0xb3, 0x77, 0xc8, 0xa7, - 0x8a, 0x9b, 0x30, 0x28, 0x7d, 0x8c, 0xe8, 0xf3, 0x87, 0x82, 0xad, 0x74, 0x88, 0xea, 0xc0, 0x49, - 0x2e, 0xbd, 0x8f, 0x89, 0xd0, 0x63, 0x42, 0x3e, 0xe4, 0x73, 0xce, 0xe1, 0x10, 0xf2, 0x21, 0x9f, - 0xbc, 0xde, 0xa5, 0x42, 0xfe, 0xcf, 0x19, 0x38, 0x9e, 0x52, 0x39, 0xb9, 0x08, 0x7d, 0xd5, 0xf6, - 0x12, 0x06, 0xb6, 0xca, 0x84, 0x1e, 0xbd, 0x6e, 0x7b, 0x09, 0x63, 0x5a, 0x69, 0x3e, 0x92, 0x2c, - 0xe2, 0xf3, 0xf5, 0xf9, 0x4a, 0x79, 0x42, 0x48, 0x55, 0x95, 0x1e, 0xe2, 0x33, 0x70, 0xda, 0x97, - 0x05, 0x4f, 0xdc, 0x6d, 0xd3, 0xa8, 0xc7, 0x9e, 0xb8, 0xb3, 0x32, 0xe4, 0xa3, 0x30, 0x50, 0x7a, - 0xab, 0xed, 0x50, 0xe4, 0xcb, 0x25, 0xfe, 0x9e, 0x80, 0xaf, 0x8f, 0x48, 0xe3, 0xcc, 0x5f, 0xeb, - 0x33, 0x8a, 0x38, 0xef, 0x90, 0xa1, 0xfa, 0xd9, 0x0c, 0x9c, 0xed, 0xdc, 0x3a, 0xf2, 0x7e, 0xe8, - 0x63, 0x3b, 0xf3, 0x92, 0x36, 0x27, 0x3e, 0x9d, 0x67, 0x00, 0xb5, 0x1b, 0xb4, 0xa6, 0x3b, 0xb2, - 0xb1, 0xef, 0x93, 0x91, 0x97, 0x61, 0xb0, 0xe2, 0xba, 0x6d, 0xea, 0x54, 0x9f, 0xbe, 0xa5, 0x55, - 0xc4, 0x9e, 0x10, 0xf7, 0x1c, 0x26, 0x82, 0x6b, 0xee, 0xd3, 0xb1, 0xd0, 0x55, 0x32, 0xbd, 0xfa, - 0xa9, 0x0c, 0x9c, 0xeb, 0xf6, 0x55, 0xe4, 0x69, 0xe8, 0x5f, 0xa4, 0x96, 0x6e, 0x79, 0x95, 0xb2, - 0x68, 0x12, 0x6e, 0xb1, 0x3c, 0x84, 0x45, 0x77, 0x0a, 0x01, 0x21, 0x2b, 0xc4, 0x8f, 0x04, 0x03, - 0x1f, 0x04, 0x7e, 0x7c, 0x89, 0xb0, 0x58, 0x21, 0x9f, 0x50, 0xfd, 0xa9, 0x25, 0xe8, 0x9d, 0xb7, - 0xe8, 0xfc, 0x32, 0x79, 0x0a, 0x06, 0x98, 0xee, 0x63, 0xfa, 0x7d, 0x31, 0xd0, 0xc6, 0xe4, 0x01, - 0x83, 0x88, 0xe9, 0x1e, 0x2d, 0xa4, 0x22, 0xd7, 0xe5, 0x9c, 0xdf, 0x42, 0x1d, 0x88, 0x5c, 0x86, - 0x63, 0xa6, 0x7b, 0x34, 0x39, 0x37, 0xf8, 0x75, 0x39, 0xd7, 0xb2, 0xe8, 0xec, 0x48, 0x29, 0x8e, - 0xf1, 0x4b, 0x89, 0x69, 0x60, 0x26, 0x2d, 0x21, 0x71, 0xdc, 0x26, 0x48, 0x52, 0x4c, 0xf7, 0x68, - 0xe9, 0x89, 0x8c, 0x87, 0x64, 0x37, 0xa6, 0xf8, 0x2d, 0xa4, 0x8c, 0x9b, 0xee, 0xd1, 0x22, 0xb4, - 0xe4, 0x39, 0x18, 0x14, 0xbf, 0x5f, 0xb3, 0x4d, 0x2b, 0x1e, 0xc3, 0x42, 0x42, 0x4d, 0xf7, 0x68, - 0x32, 0xa5, 0x54, 0xe9, 0x82, 0x63, 0x5a, 0x9e, 0x78, 0x19, 0x17, 0xaf, 0x14, 0x71, 0x52, 0xa5, - 0xf8, 0x9b, 0xbc, 0x0c, 0xc3, 0x41, 0x70, 0x90, 0x37, 0x69, 0xdd, 0x13, 0x47, 0x3a, 0x27, 0x63, - 0x85, 0x39, 0x72, 0xba, 0x47, 0x8b, 0x52, 0x93, 0x4b, 0x50, 0xd0, 0xa8, 0x6b, 0xbe, 0xe5, 0xdf, - 0x5f, 0x8c, 0x48, 0xd3, 0x99, 0xf9, 0x16, 0x93, 0x92, 0xc0, 0xb3, 0xde, 0x09, 0x2f, 0x4c, 0xc4, - 0x01, 0x0c, 0x89, 0xd5, 0x32, 0x69, 0x19, 0xac, 0x77, 0xa4, 0xdb, 0xb2, 0x57, 0xc3, 0x90, 0x29, - 0x22, 0xd1, 0xda, 0x60, 0xfc, 0x6d, 0xaa, 0x8c, 0x9d, 0xee, 0xd1, 0x62, 0xf4, 0x92, 0x54, 0xcb, - 0xa6, 0x7b, 0x57, 0x44, 0xa9, 0x8b, 0x4b, 0x95, 0xa1, 0x24, 0xa9, 0xb2, 0x9f, 0x52, 0xd5, 0x73, - 0xd4, 0x5b, 0xb3, 0x9d, 0xbb, 0x22, 0x26, 0x5d, 0xbc, 0x6a, 0x81, 0x95, 0xaa, 0x16, 0x10, 0xb9, - 0x6a, 0xb6, 0xc8, 0x8c, 0xa4, 0x57, 0xad, 0x7b, 0xba, 0x5c, 0x35, 0xdf, 0x5f, 0xfa, 0x9d, 0x34, - 0x43, 0xf5, 0x7b, 0x3c, 0xdf, 0x6d, 0xb2, 0x43, 0x11, 0x27, 0x75, 0x28, 0xfe, 0x66, 0x95, 0x4a, - 0x39, 0x4d, 0x45, 0x42, 0xdb, 0xa0, 0x52, 0x09, 0xc5, 0x2a, 0x95, 0xb3, 0x9f, 0x5e, 0x97, 0x53, - 0x7d, 0x2a, 0x63, 0xd1, 0x0e, 0x0a, 0x31, 0xac, 0x83, 0xa4, 0x94, 0xa0, 0x45, 0x4c, 0x23, 0xa8, - 0x10, 0x24, 0x1f, 0x0c, 0x5a, 0x38, 0xb1, 0x30, 0xdd, 0xa3, 0x61, 0x82, 0x41, 0x95, 0x27, 0xa8, - 0x54, 0x8e, 0x23, 0xc5, 0x90, 0x4f, 0xc1, 0x60, 0xd3, 0x3d, 0x1a, 0x4f, 0x5e, 0xf9, 0x94, 0x94, - 0x0a, 0x4a, 0x39, 0x11, 0x9d, 0x22, 0x02, 0x04, 0x9b, 0x22, 0xc2, 0x84, 0x51, 0x53, 0xc9, 0x74, - 0x49, 0xca, 0xc9, 0xe8, 0x8a, 0x1a, 0xc7, 0x4f, 0xf7, 0x68, 0xc9, 0x14, 0x4b, 0xcf, 0x45, 0x32, - 0x08, 0x29, 0xa7, 0x62, 0x81, 0x63, 0x42, 0x14, 0x13, 0x97, 0x9c, 0x6b, 0x68, 0x3e, 0x35, 0xe7, - 0xb7, 0x72, 0x3a, 0xba, 0x1c, 0xa7, 0x90, 0x4c, 0xf7, 0x68, 0xa9, 0xd9, 0xc2, 0x27, 0x12, 0x79, - 0x7c, 0x14, 0x25, 0x7a, 0x59, 0x1b, 0x43, 0x4f, 0xf7, 0x68, 0x89, 0xcc, 0x3f, 0xd7, 0xe5, 0x04, - 0x3a, 0xca, 0x99, 0x68, 0x27, 0x86, 0x18, 0xd6, 0x89, 0x52, 0xa2, 0x9d, 0xeb, 0x72, 0x52, 0x15, - 0xe5, 0x6c, 0xb2, 0x54, 0x38, 0x73, 0x4a, 0xc9, 0x57, 0xb4, 0xf4, 0x3c, 0x11, 0xca, 0x43, 0x22, - 0x53, 0x9f, 0x28, 0x9f, 0x46, 0x33, 0xdd, 0xa3, 0xa5, 0xe7, 0x98, 0xd0, 0xd2, 0x13, 0x2c, 0x28, - 0xe7, 0xba, 0xf1, 0x0c, 0x5a, 0x97, 0x9e, 0x9c, 0x41, 0xef, 0x12, 0xee, 0x5e, 0x39, 0x1f, 0x8d, - 0x5a, 0xd9, 0x91, 0x70, 0xba, 0x47, 0xeb, 0x12, 0x34, 0xff, 0x56, 0x87, 0xd8, 0xf3, 0xca, 0x85, - 0x68, 0xa2, 0xce, 0x54, 0xa2, 0xe9, 0x1e, 0xad, 0x43, 0xe4, 0xfa, 0x5b, 0x1d, 0x42, 0x93, 0x2b, - 0xc5, 0xae, 0x6c, 0x03, 0x79, 0x74, 0x08, 0x6c, 0x3e, 0x9f, 0x1a, 0xd5, 0x5b, 0x79, 0x38, 0xaa, - 0xba, 0x29, 0x24, 0x4c, 0x75, 0xd3, 0xe2, 0x81, 0xcf, 0xa7, 0x86, 0xa1, 0x56, 0x1e, 0xe9, 0xc2, - 0x30, 0x68, 0x63, 0x6a, 0x00, 0xeb, 0xf9, 0xd4, 0x38, 0xd0, 0x8a, 0x1a, 0x65, 0x98, 0x42, 0xc2, - 0x18, 0xa6, 0x45, 0x90, 0x9e, 0x4f, 0x0d, 0x17, 0xac, 0x3c, 0xda, 0x85, 0x61, 0xd8, 0xc2, 0xb4, - 0x40, 0xc3, 0xcf, 0x45, 0xe2, 0xf5, 0x2a, 0xef, 0x89, 0xce, 0x1b, 0x12, 0x8a, 0xcd, 0x1b, 0x72, - 0x64, 0xdf, 0x89, 0x44, 0x44, 0x42, 0xe5, 0xb1, 0xe8, 0x30, 0x8f, 0xa1, 0xd9, 0x30, 0x8f, 0xc7, - 0x30, 0x9c, 0x48, 0x44, 0x66, 0x53, 0x2e, 0x76, 0x62, 0x82, 0xe8, 0x28, 0x13, 0x1e, 0xcb, 0xad, - 0x92, 0x12, 0x1a, 0x4c, 0x79, 0x6f, 0xd4, 0xd1, 0x30, 0x41, 0x30, 0xdd, 0xa3, 0xa5, 0x04, 0x14, - 0xd3, 0xd2, 0xe3, 0x60, 0x28, 0x97, 0xa2, 0xc3, 0x36, 0x8d, 0x86, 0x0d, 0xdb, 0xd4, 0x18, 0x1a, - 0x33, 0x69, 0xde, 0xd0, 0xca, 0xe5, 0xa8, 0x61, 0x96, 0xa4, 0x60, 0x86, 0x59, 0x8a, 0x17, 0xb5, - 0x96, 0x1e, 0xd9, 0x41, 0x79, 0xbc, 0x6b, 0x0b, 0x91, 0x26, 0xa5, 0x85, 0x3c, 0xd0, 0x41, 0x68, - 0x3b, 0xdd, 0x6a, 0x35, 0x6c, 0xdd, 0x50, 0xde, 0x97, 0x6a, 0x3b, 0x71, 0xa4, 0x64, 0x3b, 0x71, - 0x00, 0x5b, 0xe5, 0x65, 0xa7, 0x5b, 0xe5, 0x89, 0xe8, 0x2a, 0x2f, 0xe3, 0xd8, 0x2a, 0x1f, 0x71, - 0xd0, 0x9d, 0x48, 0x38, 0xa8, 0x2a, 0x4f, 0x46, 0x15, 0x20, 0x86, 0x66, 0x0a, 0x10, 0x77, 0x69, - 0xfd, 0x78, 0x67, 0x97, 0x4e, 0xe5, 0x0a, 0x72, 0x7b, 0xd8, 0xe7, 0xd6, 0x89, 0x6e, 0xba, 0x47, - 0xeb, 0xec, 0x16, 0x5a, 0x49, 0xf1, 0xd0, 0x54, 0xae, 0x46, 0x15, 0x2c, 0x41, 0xc0, 0x14, 0x2c, - 0xe9, 0xd7, 0x59, 0x49, 0x71, 0xb1, 0x54, 0xde, 0xdf, 0x91, 0x55, 0xf0, 0xcd, 0x29, 0x8e, 0x99, - 0xd7, 0x65, 0x1f, 0x49, 0xe5, 0xa9, 0xe8, 0x62, 0x17, 0x62, 0xd8, 0x62, 0x27, 0xf9, 0x52, 0x5e, - 0x97, 0xbd, 0x03, 0x95, 0x6b, 0xc9, 0x52, 0xe1, 0x12, 0x29, 0x79, 0x11, 0x6a, 0xe9, 0x4e, 0x75, - 0xca, 0xd3, 0x51, 0xad, 0x4b, 0xa3, 0x61, 0x5a, 0x97, 0xea, 0x90, 0x37, 0x95, 0xf4, 0x8d, 0x53, - 0xae, 0xc7, 0xcf, 0x12, 0xa2, 0x78, 0x66, 0xf9, 0x24, 0xfc, 0xe9, 0x5e, 0x8d, 0x07, 0x69, 0x52, - 0x9e, 0x89, 0x5d, 0x66, 0x44, 0xb0, 0xcc, 0xbe, 0x8d, 0x05, 0x75, 0x7a, 0x35, 0x1e, 0xd7, 0x48, - 0x79, 0x36, 0x9d, 0x43, 0xa0, 0x2b, 0xf1, 0x38, 0x48, 0xaf, 0xc6, 0x43, 0x01, 0x29, 0xcf, 0xa5, - 0x73, 0x08, 0xa4, 0x1b, 0x0f, 0x1d, 0xf4, 0x94, 0x14, 0x9c, 0x58, 0xf9, 0x40, 0xd4, 0x74, 0x0c, - 0x10, 0xcc, 0x74, 0x0c, 0x43, 0x18, 0x3f, 0x25, 0x05, 0xf5, 0x55, 0x9e, 0x4f, 0x14, 0x09, 0x1a, - 0x2b, 0x85, 0xfe, 0x7d, 0x4a, 0x0a, 0x86, 0xab, 0xbc, 0x90, 0x28, 0x12, 0xb4, 0x4e, 0x0a, 0x99, - 0x6b, 0x74, 0x7b, 0x35, 0xa5, 0xbc, 0x18, 0x3d, 0xe2, 0xe8, 0x4c, 0x39, 0xdd, 0xa3, 0x75, 0x7b, - 0x7d, 0xf5, 0xf1, 0xce, 0x9e, 0x86, 0xca, 0x4b, 0xd1, 0x21, 0xdc, 0x89, 0x8e, 0x0d, 0xe1, 0x8e, - 0xde, 0x8a, 0x2f, 0xc7, 0x5e, 0x50, 0x2b, 0x2f, 0x47, 0xa7, 0xb8, 0x08, 0x92, 0x4d, 0x71, 0xf1, - 0xf7, 0xd6, 0x91, 0xa7, 0xc1, 0xca, 0x07, 0xa3, 0x53, 0x9c, 0x8c, 0x63, 0x53, 0x5c, 0xe4, 0x19, - 0xf1, 0x44, 0xe2, 0xc5, 0xaa, 0xf2, 0x4a, 0x74, 0x8a, 0x8b, 0xa1, 0xd9, 0x14, 0x17, 0x7f, 0xe3, - 0xfa, 0x72, 0xec, 0xe1, 0xa6, 0xf2, 0x6a, 0x7a, 0xfb, 0x11, 0x29, 0xb7, 0x9f, 0x3f, 0xf3, 0xd4, - 0xd2, 0x5f, 0x20, 0x2a, 0xa5, 0xe8, 0xf8, 0x4d, 0xa3, 0x61, 0xe3, 0x37, 0xf5, 0xf5, 0x62, 0x7c, - 0xe3, 0x20, 0xb4, 0x6a, 0xbc, 0xcb, 0xc6, 0x21, 0x34, 0x45, 0x52, 0xc0, 0x91, 0x3d, 0x32, 0xdf, - 0x08, 0x4d, 0x74, 0xd8, 0x23, 0xfb, 0xdb, 0xa0, 0x18, 0x3d, 0x9b, 0x5d, 0x13, 0x8e, 0x6f, 0x4a, - 0x39, 0x3a, 0xbb, 0x26, 0x08, 0xd8, 0xec, 0x9a, 0x74, 0x97, 0x9b, 0x82, 0x51, 0xa1, 0x45, 0xdc, - 0x9f, 0xcf, 0xb4, 0x56, 0x94, 0xc9, 0xd8, 0x03, 0xa0, 0x18, 0x9e, 0xcd, 0x4e, 0x71, 0x18, 0xae, - 0xd7, 0x1c, 0x36, 0xd1, 0x30, 0x5b, 0x4b, 0xb6, 0xee, 0x18, 0x55, 0x6a, 0x19, 0xca, 0x54, 0x6c, - 0xbd, 0x4e, 0xa1, 0xc1, 0xf5, 0x3a, 0x05, 0x8e, 0x81, 0x89, 0x62, 0x70, 0x8d, 0xd6, 0xa9, 0x79, - 0x8f, 0x2a, 0x37, 0x90, 0x6d, 0xb1, 0x13, 0x5b, 0x41, 0x36, 0xdd, 0xa3, 0x75, 0xe2, 0xc0, 0x6c, - 0xf5, 0xd9, 0xf5, 0xea, 0xeb, 0x33, 0xc1, 0xa3, 0xd7, 0x05, 0x87, 0xb6, 0x74, 0x87, 0x2a, 0xd3, - 0x51, 0x5b, 0x3d, 0x95, 0x88, 0xd9, 0xea, 0xa9, 0x88, 0x24, 0x5b, 0x7f, 0x2c, 0x54, 0xba, 0xb1, - 0x0d, 0x47, 0x44, 0x7a, 0x69, 0x36, 0x3b, 0x45, 0x11, 0x4c, 0x40, 0x33, 0xb6, 0xb5, 0x82, 0x27, - 0x15, 0xaf, 0x45, 0x67, 0xa7, 0xce, 0x94, 0x6c, 0x76, 0xea, 0x8c, 0x65, 0xaa, 0x1e, 0xc5, 0xf2, - 0x31, 0x78, 0x33, 0xaa, 0xea, 0x29, 0x24, 0x4c, 0xd5, 0x53, 0xc0, 0x49, 0x86, 0x1a, 0x75, 0xa9, - 0xa7, 0xcc, 0x74, 0x63, 0x88, 0x24, 0x49, 0x86, 0x08, 0x4e, 0x32, 0x9c, 0xa2, 0x5e, 0x7d, 0x55, - 0x99, 0xed, 0xc6, 0x10, 0x49, 0x92, 0x0c, 0x11, 0xcc, 0x36, 0x9b, 0x51, 0xf0, 0x78, 0xbb, 0x71, - 0xd7, 0xef, 0xb3, 0xb9, 0xe8, 0x66, 0xb3, 0x23, 0x21, 0xdb, 0x6c, 0x76, 0x44, 0x92, 0x4f, 0x6d, - 0xdb, 0x31, 0x53, 0x99, 0xc7, 0x0a, 0xaf, 0x84, 0x76, 0xc1, 0x76, 0x4a, 0x4d, 0xf7, 0x68, 0xdb, - 0x75, 0xfc, 0x7c, 0x5f, 0xe0, 0x0a, 0xa5, 0x2c, 0x60, 0x55, 0xc7, 0x82, 0xb3, 0x0a, 0x0e, 0x9e, - 0xee, 0xd1, 0x02, 0x67, 0xa9, 0xe7, 0x60, 0x10, 0x3f, 0xaa, 0x62, 0x99, 0x5e, 0x79, 0x5c, 0x79, - 0x3d, 0xba, 0x65, 0x92, 0x50, 0x6c, 0xcb, 0x24, 0xfd, 0x64, 0x93, 0x38, 0xfe, 0xe4, 0x53, 0x4c, - 0x79, 0x5c, 0xd1, 0xa2, 0x93, 0x78, 0x04, 0xc9, 0x26, 0xf1, 0x08, 0x20, 0xa8, 0xb7, 0xec, 0xd8, - 0xad, 0xf2, 0xb8, 0x52, 0x4d, 0xa9, 0x97, 0xa3, 0x82, 0x7a, 0xf9, 0xcf, 0xa0, 0xde, 0xea, 0x6a, - 0xdb, 0x2b, 0xb3, 0x6f, 0x5c, 0x4c, 0xa9, 0xd7, 0x47, 0x06, 0xf5, 0xfa, 0x00, 0x36, 0x15, 0x22, - 0x60, 0xc1, 0xb1, 0xd9, 0xa4, 0x7d, 0xd3, 0x6c, 0x34, 0x94, 0x5b, 0xd1, 0xa9, 0x30, 0x8e, 0x67, - 0x53, 0x61, 0x1c, 0xc6, 0x4c, 0x4f, 0xde, 0x2a, 0xba, 0xd4, 0x5e, 0x51, 0x6e, 0x47, 0x4d, 0xcf, - 0x10, 0xc3, 0x4c, 0xcf, 0xf0, 0x17, 0xee, 0x2e, 0xd8, 0x2f, 0x8d, 0x2e, 0x3b, 0xd4, 0x5d, 0x55, - 0xee, 0xc4, 0x76, 0x17, 0x12, 0x0e, 0x77, 0x17, 0xd2, 0x6f, 0xb2, 0x02, 0x0f, 0x45, 0x16, 0x1a, - 0xff, 0xee, 0xa9, 0x4a, 0x75, 0xa7, 0xbe, 0xaa, 0x7c, 0x08, 0x59, 0x3d, 0x9a, 0xba, 0x54, 0x45, - 0x49, 0xa7, 0x7b, 0xb4, 0x6e, 0x9c, 0x70, 0x5b, 0xfe, 0xfa, 0x0c, 0x8f, 0x20, 0xa8, 0x2d, 0x4c, - 0xf8, 0x9b, 0xd0, 0x37, 0x62, 0xdb, 0xf2, 0x24, 0x09, 0x6e, 0xcb, 0x93, 0x60, 0xd2, 0x82, 0x0b, - 0xb1, 0xad, 0xda, 0xac, 0xde, 0x60, 0xfb, 0x12, 0x6a, 0x2c, 0xe8, 0xf5, 0xbb, 0xd4, 0x53, 0x3e, - 0x8c, 0xbc, 0x2f, 0x76, 0xd8, 0xf0, 0xc5, 0xa8, 0xa7, 0x7b, 0xb4, 0x2d, 0xf8, 0x11, 0x15, 0xf2, - 0xd5, 0xa9, 0xc5, 0x05, 0xe5, 0x23, 0xd1, 0xf3, 0x4d, 0x06, 0x9b, 0xee, 0xd1, 0x10, 0xc7, 0xac, - 0xb4, 0x5b, 0xad, 0x15, 0x47, 0x37, 0x28, 0x37, 0xb4, 0xd0, 0x76, 0x13, 0x06, 0xe8, 0x47, 0xa3, - 0x56, 0x5a, 0x27, 0x3a, 0x66, 0xa5, 0x75, 0xc2, 0x31, 0x45, 0x8d, 0x04, 0xcb, 0x57, 0x3e, 0x16, - 0x55, 0xd4, 0x08, 0x92, 0x29, 0x6a, 0x34, 0xb4, 0xfe, 0x87, 0xe0, 0x54, 0xb0, 0x9f, 0x17, 0xeb, - 0x2f, 0xef, 0x34, 0xe5, 0xe3, 0xc8, 0xe7, 0x42, 0xe2, 0x32, 0x20, 0x42, 0x35, 0xdd, 0xa3, 0x75, - 0x28, 0xcf, 0x56, 0xdc, 0x44, 0x1e, 0x18, 0x61, 0x5e, 0x7c, 0x5b, 0x74, 0xc5, 0xed, 0x40, 0xc6, - 0x56, 0xdc, 0x0e, 0xa8, 0x54, 0xe6, 0x42, 0xa8, 0xfa, 0x16, 0xcc, 0x03, 0x99, 0x76, 0xe2, 0x90, - 0xca, 0x5c, 0x58, 0x6a, 0x4b, 0x5b, 0x30, 0x0f, 0xac, 0xb5, 0x4e, 0x1c, 0xc8, 0x25, 0x28, 0x54, - 0xab, 0xb3, 0x5a, 0xdb, 0x52, 0xea, 0x31, 0x1f, 0x30, 0x84, 0x4e, 0xf7, 0x68, 0x02, 0xcf, 0xcc, - 0xa0, 0xc9, 0x86, 0xee, 0x7a, 0x66, 0xdd, 0xc5, 0x11, 0xe3, 0x8f, 0x10, 0x23, 0x6a, 0x06, 0xa5, - 0xd1, 0x30, 0x33, 0x28, 0x0d, 0xce, 0xec, 0xc5, 0x09, 0xdd, 0x75, 0x75, 0xcb, 0x70, 0xf4, 0x71, - 0x5c, 0x26, 0x68, 0xec, 0x79, 0x40, 0x04, 0xcb, 0xec, 0xc5, 0x28, 0x04, 0x0f, 0xdf, 0x7d, 0x88, - 0x6f, 0xe6, 0x2c, 0xc7, 0x0e, 0xdf, 0x63, 0x78, 0x3c, 0x7c, 0x8f, 0xc1, 0xd0, 0xee, 0xf4, 0x61, - 0x1a, 0x5d, 0x31, 0x99, 0x88, 0x94, 0x95, 0x98, 0xdd, 0x19, 0x27, 0x40, 0xbb, 0x33, 0x0e, 0x8c, - 0x34, 0xc9, 0x5f, 0x6e, 0x57, 0x3b, 0x34, 0x29, 0x5c, 0x65, 0x13, 0x65, 0xd8, 0xfa, 0x1d, 0x0e, - 0x8e, 0xf2, 0xba, 0xa5, 0x37, 0xed, 0xf2, 0xb8, 0x2f, 0x75, 0x33, 0xba, 0x7e, 0x77, 0x24, 0x64, - 0xeb, 0x77, 0x47, 0x24, 0x9b, 0x5d, 0xfd, 0x8d, 0xd6, 0xaa, 0xee, 0x50, 0xa3, 0x6c, 0x3a, 0x78, - 0xb2, 0xb8, 0xce, 0xb7, 0x86, 0x6f, 0x46, 0x67, 0xd7, 0x2e, 0xa4, 0x6c, 0x76, 0xed, 0x82, 0x66, - 0x46, 0x5e, 0x3a, 0x5a, 0xa3, 0xba, 0xa1, 0xdc, 0x8d, 0x1a, 0x79, 0x9d, 0x29, 0x99, 0x91, 0xd7, - 0x19, 0xdb, 0xf9, 0x73, 0xee, 0x38, 0xa6, 0x47, 0x95, 0xc6, 0x76, 0x3e, 0x07, 0x49, 0x3b, 0x7f, - 0x0e, 0xa2, 0xd9, 0x86, 0x30, 0xde, 0x21, 0xcd, 0xe8, 0x86, 0x30, 0xd9, 0x0d, 0xf1, 0x12, 0xcc, - 0x62, 0x11, 0xaf, 0x44, 0x14, 0x2b, 0x6a, 0xb1, 0x08, 0x30, 0xb3, 0x58, 0xc2, 0x77, 0x24, 0x91, - 0x07, 0x06, 0x8a, 0x1d, 0x5d, 0x43, 0x65, 0x1c, 0x5b, 0x43, 0x23, 0x8f, 0x11, 0x9e, 0x8b, 0x78, - 0xcf, 0x2a, 0xad, 0xa8, 0xd5, 0x21, 0xa1, 0x98, 0xd5, 0x21, 0xfb, 0xd9, 0x4e, 0xc0, 0x31, 0xbc, - 0x05, 0xd7, 0xda, 0xc1, 0x3d, 0xce, 0x27, 0xa2, 0x9f, 0x19, 0x43, 0xb3, 0xcf, 0x8c, 0x81, 0x22, - 0x4c, 0xc4, 0xb4, 0xe5, 0x74, 0x60, 0x12, 0x9e, 0x0f, 0xc6, 0x40, 0x64, 0x06, 0x48, 0xb5, 0x34, - 0x3b, 0x53, 0x31, 0x16, 0xe4, 0x2b, 0x32, 0x37, 0x7a, 0x02, 0x9b, 0xa4, 0x98, 0xee, 0xd1, 0x52, - 0xca, 0x91, 0x37, 0xe1, 0x9c, 0x80, 0x8a, 0x27, 0x80, 0x98, 0x2e, 0xda, 0x08, 0x16, 0x04, 0x2f, - 0xea, 0x9d, 0xd1, 0x8d, 0x76, 0xba, 0x47, 0xeb, 0xca, 0xab, 0x73, 0x5d, 0x62, 0x7d, 0x68, 0x6f, - 0xa7, 0xae, 0x60, 0x91, 0xe8, 0xca, 0xab, 0x73, 0x5d, 0x42, 0xee, 0xf7, 0xb6, 0x53, 0x57, 0xd0, - 0x09, 0x5d, 0x79, 0x11, 0x17, 0x8a, 0xdd, 0xf0, 0xa5, 0x46, 0x43, 0x59, 0xc3, 0xea, 0xde, 0xbb, - 0x9d, 0xea, 0x4a, 0x68, 0x70, 0x6e, 0xc5, 0x91, 0xcd, 0xd2, 0xf3, 0x2d, 0x6a, 0x55, 0x23, 0x0b, - 0xd0, 0xfd, 0xe8, 0x2c, 0x9d, 0x20, 0x60, 0xb3, 0x74, 0x02, 0xc8, 0x06, 0x94, 0xec, 0x84, 0xad, - 0xac, 0x47, 0x07, 0x94, 0x8c, 0x63, 0x03, 0x2a, 0xe2, 0xb0, 0x3d, 0x0f, 0xc7, 0xe7, 0xef, 0x7a, - 0xba, 0x6f, 0x41, 0xba, 0xa2, 0x2b, 0xdf, 0x8a, 0x5d, 0x32, 0x25, 0x49, 0xf0, 0x92, 0x29, 0x09, - 0x66, 0x63, 0x84, 0x81, 0xab, 0xeb, 0x56, 0x7d, 0x4a, 0x37, 0x1b, 0x6d, 0x87, 0x2a, 0xff, 0x47, - 0x74, 0x8c, 0xc4, 0xd0, 0x6c, 0x8c, 0xc4, 0x40, 0x6c, 0x81, 0x66, 0xa0, 0x92, 0xeb, 0x9a, 0x2b, - 0x96, 0xd8, 0x57, 0xb6, 0x1b, 0x9e, 0xf2, 0x7f, 0x46, 0x17, 0xe8, 0x34, 0x1a, 0xb6, 0x40, 0xa7, - 0xc1, 0xf1, 0xd4, 0x29, 0x25, 0x95, 0xba, 0xf2, 0x7f, 0xc5, 0x4e, 0x9d, 0x52, 0x68, 0xf0, 0xd4, - 0x29, 0x2d, 0x0d, 0xfb, 0x14, 0x8c, 0x72, 0x9b, 0x6c, 0xc6, 0x0c, 0xee, 0xaa, 0xff, 0xef, 0xe8, - 0xfa, 0x18, 0xc7, 0xb3, 0xf5, 0x31, 0x0e, 0x8b, 0xf2, 0x11, 0x5d, 0xf0, 0xff, 0x74, 0xe2, 0x13, - 0xc8, 0x3f, 0x51, 0x86, 0xdc, 0x90, 0xf9, 0x88, 0x91, 0xf2, 0xed, 0x99, 0x4e, 0x8c, 0x82, 0xe1, - 0x91, 0x28, 0x14, 0x65, 0xa4, 0xd1, 0x7b, 0x26, 0x5d, 0x53, 0x3e, 0xd9, 0x91, 0x11, 0x27, 0x88, - 0x32, 0xe2, 0x30, 0xf2, 0x06, 0x9c, 0x0a, 0x61, 0xb3, 0xb4, 0xb9, 0x14, 0xcc, 0x4c, 0xdf, 0x91, - 0x89, 0x9a, 0xc1, 0xe9, 0x64, 0xcc, 0x0c, 0x4e, 0xc7, 0xa4, 0xb1, 0x16, 0xa2, 0xfb, 0x7f, 0xb7, - 0x60, 0x1d, 0x48, 0xb0, 0x03, 0x83, 0x34, 0xd6, 0x42, 0x9a, 0xdf, 0xb9, 0x05, 0xeb, 0x40, 0xa6, - 0x1d, 0x18, 0x90, 0x4f, 0x67, 0xe0, 0x62, 0x3a, 0xaa, 0xd4, 0x68, 0x4c, 0xd9, 0x4e, 0x88, 0x53, - 0xbe, 0x2b, 0x13, 0x3d, 0x68, 0xd8, 0x5e, 0xb1, 0xe9, 0x1e, 0x6d, 0x9b, 0x15, 0x90, 0x0f, 0xc2, - 0x70, 0xa9, 0x6d, 0x98, 0x1e, 0x5e, 0xbc, 0x31, 0xc3, 0xf9, 0xbb, 0x33, 0xb1, 0x2d, 0x8e, 0x8c, - 0xc5, 0x2d, 0x8e, 0x0c, 0x20, 0xaf, 0xc1, 0x58, 0x95, 0xd6, 0xdb, 0x8e, 0xe9, 0xad, 0x6b, 0x98, - 0x26, 0x9f, 0xf1, 0xf8, 0x9e, 0x4c, 0x74, 0x12, 0x4b, 0x50, 0xb0, 0x49, 0x2c, 0x01, 0x24, 0xb7, - 0x3b, 0x24, 0x53, 0x57, 0x3e, 0x95, 0xe9, 0x7a, 0x2d, 0x1f, 0xf4, 0x65, 0x87, 0x5c, 0xec, 0x0b, - 0xa9, 0xc9, 0xa9, 0x95, 0x4f, 0x67, 0xba, 0x5c, 0xa3, 0x4b, 0x33, 0x5c, 0x4a, 0x5e, 0xeb, 0x85, - 0xd4, 0xcc, 0xc1, 0xca, 0xf7, 0x66, 0xba, 0x5c, 0x7b, 0x87, 0x1c, 0xd3, 0x92, 0x0e, 0x3f, 0xc3, - 0x3d, 0x45, 0x04, 0xa3, 0xff, 0x2f, 0x93, 0x74, 0x15, 0x09, 0xca, 0x4b, 0x84, 0xac, 0xd8, 0x2d, - 0x37, 0x50, 0xfa, 0xcf, 0x64, 0x92, 0xbe, 0x79, 0x61, 0xb1, 0xf0, 0x17, 0xa1, 0x70, 0x76, 0xf2, - 0xbe, 0x47, 0x1d, 0x4b, 0x6f, 0x60, 0x77, 0x56, 0x3d, 0xdb, 0xd1, 0x57, 0xe8, 0xa4, 0xa5, 0x2f, - 0x35, 0xa8, 0xf2, 0xd9, 0x4c, 0xd4, 0x82, 0xed, 0x4c, 0xca, 0x2c, 0xd8, 0xce, 0x58, 0xb2, 0x0a, - 0x0f, 0xa5, 0x61, 0xcb, 0xa6, 0x8b, 0xf5, 0x7c, 0x2e, 0x13, 0x35, 0x61, 0xbb, 0xd0, 0x32, 0x13, - 0xb6, 0x0b, 0x9a, 0x5c, 0x83, 0x81, 0x71, 0xdb, 0x9f, 0x7e, 0xbf, 0x2f, 0xe6, 0x0c, 0x19, 0x60, - 0xa6, 0x7b, 0xb4, 0x90, 0x4c, 0x94, 0x11, 0x83, 0xfa, 0xf3, 0xc9, 0x32, 0xe1, 0xe5, 0x53, 0xf0, - 0x43, 0x94, 0x11, 0xe2, 0xfe, 0xff, 0x93, 0x65, 0xc2, 0x3b, 0xae, 0xe0, 0x07, 0x9b, 0x49, 0x78, - 0x8d, 0xb3, 0x53, 0x25, 0x66, 0xb7, 0x4d, 0xac, 0xea, 0x8d, 0x06, 0xb5, 0x56, 0xa8, 0xf2, 0x85, - 0xd8, 0x4c, 0x92, 0x4e, 0xc6, 0x66, 0x92, 0x74, 0x0c, 0xf9, 0x28, 0x9c, 0xbe, 0xad, 0x37, 0x4c, - 0x23, 0xc4, 0xf9, 0x79, 0x64, 0x95, 0xef, 0xcf, 0x44, 0x77, 0xd3, 0x1d, 0xe8, 0xd8, 0x6e, 0xba, - 0x03, 0x8a, 0xcc, 0x02, 0xc1, 0x65, 0x34, 0x98, 0x2d, 0xd8, 0xfa, 0xac, 0xfc, 0x85, 0x4c, 0xd4, - 0x4e, 0x4d, 0x92, 0x30, 0x3b, 0x35, 0x09, 0x25, 0xb5, 0xce, 0x01, 0xe9, 0x95, 0x1f, 0xc8, 0x44, - 0x4f, 0x6b, 0x3a, 0x11, 0x4e, 0xf7, 0x68, 0x9d, 0xa3, 0xda, 0xdf, 0x80, 0xd1, 0xea, 0x42, 0x65, - 0x6a, 0x6a, 0xb2, 0x7a, 0xbb, 0x52, 0x46, 0xf7, 0x5d, 0x43, 0xf9, 0xc1, 0xd8, 0x8a, 0x15, 0x27, - 0x60, 0x2b, 0x56, 0x1c, 0x46, 0x5e, 0x84, 0x21, 0xd6, 0x7e, 0x36, 0x60, 0xf0, 0x93, 0xbf, 0x98, - 0x89, 0x9a, 0x53, 0x32, 0x92, 0x99, 0x53, 0xf2, 0x6f, 0x52, 0x85, 0x13, 0x4c, 0x8a, 0x0b, 0x0e, - 0x5d, 0xa6, 0x0e, 0xb5, 0xea, 0xfe, 0x98, 0xfe, 0xa1, 0x4c, 0xd4, 0xca, 0x48, 0x23, 0x62, 0x56, - 0x46, 0x1a, 0x9c, 0xdc, 0x85, 0x73, 0xf1, 0x93, 0x20, 0xf9, 0x31, 0x95, 0xf2, 0xc3, 0x99, 0x98, - 0x31, 0xdc, 0x85, 0x18, 0x8d, 0xe1, 0x2e, 0x78, 0x62, 0xc1, 0x79, 0x71, 0xac, 0x22, 0x1c, 0x2e, - 0xe3, 0xb5, 0xfd, 0x45, 0x5e, 0xdb, 0x63, 0xa1, 0x43, 0x60, 0x17, 0xea, 0xe9, 0x1e, 0xad, 0x3b, - 0x3b, 0xa6, 0x67, 0xc9, 0xb0, 0xeb, 0xca, 0x8f, 0x64, 0xd2, 0x3d, 0x52, 0x22, 0x6e, 0xca, 0x69, - 0xf1, 0xda, 0xdf, 0xe8, 0x14, 0x34, 0x5c, 0xf9, 0xd1, 0xd8, 0x78, 0x4b, 0x27, 0x63, 0xe3, 0xad, - 0x43, 0xd4, 0xf1, 0xd7, 0x60, 0x8c, 0x2b, 0xf5, 0x82, 0x8e, 0xc3, 0xd0, 0x5a, 0xa1, 0x86, 0xf2, - 0x97, 0x62, 0xab, 0x5d, 0x82, 0x02, 0x5d, 0x7b, 0xe2, 0x40, 0x36, 0x75, 0x57, 0x5b, 0xba, 0x65, - 0xe1, 0x31, 0xab, 0xf2, 0x97, 0x63, 0x53, 0x77, 0x88, 0x42, 0xc7, 0xdd, 0xe0, 0x17, 0xd3, 0x84, - 0x6e, 0x09, 0x37, 0x94, 0x1f, 0x8b, 0x69, 0x42, 0x37, 0x62, 0xa6, 0x09, 0x5d, 0xb3, 0x77, 0xdc, - 0xee, 0xf0, 0xb0, 0x51, 0xf9, 0x52, 0x6c, 0x45, 0x4e, 0xa5, 0x62, 0x2b, 0x72, 0xfa, 0xbb, 0xc8, - 0xdb, 0x1d, 0x1e, 0x05, 0x2a, 0x3f, 0xde, 0x9d, 0x6f, 0xb8, 0xd2, 0xa7, 0xbf, 0x29, 0xbc, 0xdd, - 0xe1, 0x41, 0x9d, 0xf2, 0x13, 0xdd, 0xf9, 0x86, 0x8e, 0x7d, 0xe9, 0xef, 0xf1, 0x6a, 0x9d, 0x1f, - 0xa3, 0x29, 0x3f, 0x19, 0x9f, 0xba, 0x3a, 0x10, 0xe2, 0xd4, 0xd5, 0xe9, 0x45, 0xdb, 0x12, 0x9c, - 0xe1, 0x1a, 0x72, 0xc3, 0xd1, 0x5b, 0xab, 0x55, 0xea, 0x79, 0xa6, 0xb5, 0xe2, 0xef, 0xc4, 0x7e, - 0x2a, 0x13, 0x3b, 0x1e, 0xeb, 0x44, 0x89, 0xc7, 0x63, 0x9d, 0x90, 0x4c, 0x79, 0x13, 0xcf, 0xce, - 0x94, 0x9f, 0x8e, 0x29, 0x6f, 0x82, 0x82, 0x29, 0x6f, 0xf2, 0xb5, 0xda, 0x6b, 0x29, 0xaf, 0xab, - 0x94, 0xbf, 0xd2, 0x99, 0x57, 0xd0, 0xbe, 0x94, 0x47, 0x59, 0xaf, 0xa5, 0x3c, 0x22, 0x52, 0xfe, - 0x6a, 0x67, 0x5e, 0xa1, 0x0f, 0x52, 0x02, 0x38, 0xde, 0x07, 0xbd, 0xb8, 0xab, 0x55, 0xbf, 0x94, - 0x81, 0xa1, 0xaa, 0xe7, 0x50, 0xbd, 0x29, 0x22, 0x4a, 0x9c, 0x85, 0x7e, 0xee, 0x1e, 0xe6, 0xbf, - 0xd0, 0xd0, 0x82, 0xdf, 0xe4, 0x22, 0x8c, 0xcc, 0xe8, 0xae, 0x87, 0x25, 0x2b, 0x96, 0x41, 0xef, - 0xe3, 0xd3, 0x88, 0x9c, 0x16, 0x83, 0x92, 0x19, 0x4e, 0xc7, 0xcb, 0x61, 0xfc, 0x9f, 0xdc, 0x96, - 0x81, 0x14, 0xfa, 0xdf, 0xde, 0x28, 0xf6, 0x60, 0xdc, 0x84, 0x58, 0x59, 0xf5, 0x6b, 0x19, 0x48, - 0x38, 0xae, 0xed, 0xfe, 0xe5, 0xd4, 0x3c, 0x1c, 0x8b, 0xc5, 0x9c, 0x12, 0xef, 0x3b, 0xb6, 0x19, - 0x92, 0x2a, 0x5e, 0x9a, 0xbc, 0x37, 0x78, 0x57, 0x70, 0x4b, 0x9b, 0x11, 0x41, 0x32, 0xfa, 0x36, - 0x37, 0x8a, 0xb9, 0xb6, 0xd3, 0xd0, 0x24, 0x94, 0x78, 0x04, 0xfd, 0xb7, 0x46, 0xc3, 0x80, 0x3a, - 0xe4, 0xa2, 0x78, 0xc6, 0x95, 0x09, 0x43, 0x6b, 0xc4, 0xd2, 0x35, 0xf2, 0x67, 0x5b, 0x1f, 0x84, - 0xa1, 0x4a, 0xb3, 0x45, 0x1d, 0xd7, 0xb6, 0x74, 0xcf, 0xf6, 0xd3, 0xc2, 0x63, 0xd8, 0x05, 0x53, - 0x82, 0xcb, 0xa1, 0x00, 0x64, 0x7a, 0x72, 0xd9, 0xcf, 0x20, 0x91, 0xc3, 0x50, 0x46, 0x18, 0x8f, - 0x33, 0x9e, 0x02, 0x90, 0x53, 0x30, 0xd2, 0x5b, 0xae, 0x8e, 0x2f, 0x50, 0x02, 0xd2, 0x36, 0x03, - 0xc8, 0xa4, 0x48, 0x41, 0x9e, 0x80, 0x02, 0x9e, 0xd8, 0xb9, 0x98, 0x19, 0x46, 0x04, 0xfc, 0x68, - 0x20, 0x44, 0x0e, 0xaf, 0xc0, 0x69, 0xc8, 0x4d, 0x18, 0x0d, 0xaf, 0x23, 0x6e, 0x38, 0x76, 0xbb, - 0xe5, 0xc7, 0x82, 0xc6, 0xd4, 0x89, 0x77, 0x03, 0x5c, 0x6d, 0x05, 0x91, 0x12, 0x8b, 0x44, 0x41, - 0x32, 0x0d, 0xc7, 0x42, 0x18, 0x13, 0x91, 0x1f, 0x83, 0x1e, 0xf3, 0xff, 0x48, 0xbc, 0x98, 0x38, - 0x23, 0xf9, 0x7f, 0x62, 0xc5, 0x48, 0x05, 0xfa, 0xfc, 0x68, 0x1f, 0xfd, 0x5b, 0x2a, 0xe9, 0x71, - 0x11, 0xed, 0xa3, 0x4f, 0x8e, 0xf3, 0xe1, 0x97, 0x27, 0x53, 0x30, 0xa2, 0xd9, 0x6d, 0x8f, 0x2e, - 0xda, 0x62, 0x1d, 0x17, 0x41, 0x8a, 0xb1, 0x4d, 0x0e, 0xc3, 0xd4, 0x3c, 0xdb, 0xcf, 0x3c, 0x29, - 0x67, 0x40, 0x8c, 0x96, 0x22, 0x73, 0x30, 0x96, 0xb8, 0xb8, 0x91, 0xf3, 0x41, 0x4a, 0x9f, 0x97, - 0x64, 0x96, 0x2c, 0x4a, 0xbe, 0x3b, 0x03, 0x85, 0x45, 0x47, 0x37, 0x3d, 0x57, 0x3c, 0x5e, 0x39, - 0x79, 0x65, 0xcd, 0xd1, 0x5b, 0x4c, 0x3f, 0xae, 0x60, 0xac, 0xaa, 0xdb, 0x7a, 0xa3, 0x4d, 0xdd, - 0xf1, 0x3b, 0xec, 0xeb, 0xfe, 0xe5, 0x46, 0xf1, 0xc5, 0x15, 0xdc, 0x1e, 0x5e, 0xa9, 0xdb, 0xcd, - 0xab, 0x2b, 0x8e, 0x7e, 0xcf, 0xf4, 0x70, 0xea, 0xd0, 0x1b, 0x57, 0x3d, 0xda, 0xc0, 0x5d, 0xe8, - 0x55, 0xbd, 0x65, 0x5e, 0xc5, 0x98, 0x88, 0x57, 0x03, 0x4e, 0xbc, 0x06, 0xa6, 0x02, 0x1e, 0xfe, - 0x25, 0xab, 0x00, 0xc7, 0x91, 0x39, 0xb6, 0x79, 0xc3, 0x4f, 0x2d, 0xb5, 0x5a, 0xe2, 0x25, 0x8c, - 0xb4, 0x77, 0xf3, 0x31, 0x5c, 0xb1, 0x03, 0x81, 0xe9, 0xad, 0x96, 0x9c, 0x71, 0x36, 0xa4, 0x63, - 0x5a, 0xb0, 0x28, 0x5a, 0xe4, 0x8b, 0x69, 0x38, 0x94, 0xb8, 0xdf, 0xd8, 0x14, 0x21, 0xc5, 0x8b, - 0x91, 0x25, 0x38, 0x26, 0xf8, 0x06, 0x51, 0x83, 0x47, 0xa2, 0xb3, 0x42, 0x0c, 0xcd, 0x95, 0x36, - 0x68, 0xa3, 0x21, 0xc0, 0x72, 0x1d, 0xb1, 0x12, 0x64, 0x3c, 0x4c, 0x65, 0x36, 0xa7, 0x37, 0xa9, - 0xab, 0x1c, 0x43, 0x8d, 0x3d, 0xb7, 0xb9, 0x51, 0x54, 0xfc, 0xf2, 0x18, 0xf8, 0x26, 0x35, 0x31, - 0x27, 0x16, 0x91, 0x79, 0x70, 0xad, 0x1f, 0x4d, 0xe1, 0x11, 0xd7, 0xf9, 0x68, 0x11, 0x32, 0x01, - 0xc3, 0x81, 0x23, 0xee, 0xad, 0x5b, 0x95, 0x32, 0x3e, 0xb5, 0x19, 0x18, 0x3f, 0xbf, 0xb9, 0x51, - 0x3c, 0x13, 0x8b, 0xeb, 0x2b, 0x33, 0x89, 0x94, 0x91, 0xde, 0xe4, 0xf1, 0xb7, 0x37, 0xb1, 0x37, - 0x79, 0xad, 0x94, 0x37, 0x79, 0x0b, 0xe4, 0x65, 0x18, 0x2c, 0xdd, 0xa9, 0x8a, 0xb7, 0x86, 0xae, - 0x72, 0x3c, 0x8c, 0x04, 0x8f, 0xb9, 0x59, 0xc5, 0xbb, 0x44, 0xb9, 0xe9, 0x32, 0x3d, 0x99, 0x84, - 0x91, 0xc8, 0x5d, 0xbe, 0xab, 0x9c, 0x40, 0x0e, 0xd8, 0x72, 0x1d, 0x31, 0x35, 0x47, 0xa0, 0x22, - 0xd9, 0x82, 0x23, 0x85, 0x98, 0xd6, 0xb0, 0xed, 0x70, 0xa3, 0x61, 0xaf, 0x69, 0x14, 0x9f, 0x35, - 0xe2, 0xc3, 0x9d, 0x7e, 0xae, 0x35, 0x86, 0x40, 0xd5, 0x1c, 0x8e, 0x8b, 0xa4, 0x07, 0x8e, 0x16, - 0x23, 0x6f, 0x02, 0xc1, 0x38, 0xdc, 0xd4, 0xf0, 0x8f, 0x76, 0x2b, 0x65, 0x57, 0x39, 0x85, 0x31, - 0xfb, 0x48, 0xfc, 0x5d, 0x6d, 0xa5, 0x3c, 0x7e, 0x51, 0x4c, 0x1f, 0x17, 0x74, 0x5e, 0xaa, 0xe6, - 0x08, 0x5c, 0xcd, 0x8c, 0x24, 0x29, 0x4b, 0xe1, 0x4a, 0xd6, 0xe0, 0xf4, 0x82, 0x43, 0xef, 0x99, - 0x76, 0xdb, 0xf5, 0x97, 0x0f, 0x7f, 0xde, 0x3a, 0xbd, 0xe5, 0xbc, 0xf5, 0x88, 0xa8, 0xf8, 0x64, - 0xcb, 0xa1, 0xf7, 0x6a, 0x7e, 0xa4, 0xb6, 0x48, 0xb4, 0xa2, 0x4e, 0xdc, 0x31, 0xd5, 0xda, 0x5b, - 0x6d, 0x87, 0x0a, 0xb8, 0x49, 0x5d, 0x45, 0x09, 0xa7, 0x5a, 0xfe, 0x42, 0xd5, 0x0c, 0x70, 0x91, - 0x54, 0x6b, 0xd1, 0x62, 0x44, 0x03, 0x72, 0x63, 0xc2, 0x3f, 0xe6, 0x2f, 0xd5, 0x79, 0x42, 0x2a, - 0xe5, 0x0c, 0x32, 0x53, 0x99, 0x58, 0x56, 0xea, 0x41, 0xd4, 0xc6, 0x9a, 0x2e, 0xf0, 0xb2, 0x58, - 0x92, 0xa5, 0xc9, 0x0c, 0x8c, 0x2e, 0x38, 0xb8, 0xe9, 0xb8, 0x49, 0xd7, 0x17, 0xec, 0x86, 0x59, - 0x5f, 0xc7, 0xf7, 0x43, 0x62, 0xaa, 0x6c, 0x71, 0x5c, 0xed, 0x2e, 0x5d, 0xaf, 0xb5, 0x10, 0x2b, - 0x2f, 0x2b, 0xf1, 0x92, 0x72, 0x14, 0xb5, 0x87, 0xb6, 0x17, 0x45, 0x8d, 0xc2, 0xa8, 0xb8, 0x24, - 0xb8, 0xef, 0x51, 0x8b, 0x2d, 0xf5, 0xae, 0x78, 0x2b, 0xa4, 0xc4, 0x2e, 0x15, 0x02, 0xbc, 0x48, - 0x15, 0xcc, 0x47, 0x19, 0x0d, 0xc0, 0x72, 0xc3, 0xe2, 0x45, 0xd4, 0xcf, 0xe5, 0xe4, 0xa9, 0x93, - 0x9c, 0x83, 0xbc, 0x14, 0xc4, 0x1b, 0xe3, 0x28, 0x61, 0xc0, 0xc3, 0xbc, 0x88, 0xec, 0x36, 0x20, - 0xcc, 0x8e, 0xe0, 0xc1, 0x2c, 0xa6, 0x66, 0x09, 0x63, 0xeb, 0x68, 0x21, 0x01, 0xa6, 0xc5, 0x68, - 0x2f, 0x35, 0xcc, 0x3a, 0x86, 0xc1, 0xcc, 0x49, 0x69, 0x31, 0x10, 0xca, 0xa3, 0x60, 0x4a, 0x24, - 0xe4, 0x1a, 0x0c, 0xfa, 0xfb, 0xd4, 0x30, 0x8e, 0x18, 0x46, 0x47, 0xf4, 0x93, 0x2a, 0xf3, 0xe0, - 0x8b, 0x12, 0x11, 0x79, 0x01, 0xd3, 0x8a, 0xfb, 0x8f, 0x91, 0x7b, 0x43, 0xf3, 0x45, 0x1e, 0xf8, - 0xb1, 0xbc, 0xe2, 0xfe, 0x9b, 0xe4, 0x71, 0x18, 0x96, 0x35, 0xc9, 0x4f, 0x04, 0x84, 0x73, 0x5e, - 0x44, 0xfd, 0xe4, 0xbe, 0x8d, 0x16, 0x21, 0xf3, 0x30, 0x96, 0x50, 0x1e, 0x11, 0x75, 0x0c, 0x93, - 0x41, 0xa6, 0x68, 0x9e, 0xbc, 0xa6, 0x26, 0xca, 0xaa, 0xdf, 0x91, 0x4d, 0xac, 0x18, 0x4c, 0x30, - 0x82, 0x4a, 0xea, 0x1c, 0x14, 0x8c, 0xcf, 0x9a, 0x0b, 0x46, 0x22, 0x22, 0x97, 0xa0, 0x3f, 0x96, - 0x59, 0x1c, 0x9f, 0xa7, 0x07, 0x69, 0xc5, 0x03, 0x2c, 0xb9, 0x26, 0xa5, 0xa6, 0x92, 0x42, 0xfc, - 0xf9, 0xa9, 0xa9, 0xe2, 0xb1, 0xee, 0x30, 0x49, 0xd5, 0xb5, 0x58, 0x14, 0x7c, 0x3f, 0x01, 0x74, - 0x72, 0xb5, 0x0a, 0xa3, 0xde, 0x07, 0xb6, 0x62, 0xef, 0x56, 0xb6, 0xa2, 0xfa, 0x6b, 0x99, 0xa4, - 0xf6, 0x93, 0xeb, 0xc9, 0x90, 0x5d, 0x3c, 0xf7, 0xb3, 0x0f, 0x94, 0x6b, 0x0d, 0x82, 0x77, 0x45, - 0x82, 0x6f, 0x65, 0x77, 0x1d, 0x7c, 0x2b, 0xb7, 0xc3, 0xe0, 0x5b, 0xea, 0xff, 0xc8, 0x77, 0x75, - 0x35, 0x3b, 0x90, 0x20, 0x0d, 0xcf, 0xb3, 0xfd, 0x0e, 0xab, 0xbd, 0xe4, 0x26, 0xac, 0x76, 0xee, - 0x49, 0x53, 0xd3, 0xf9, 0xa8, 0x71, 0xb5, 0x28, 0x25, 0x79, 0x05, 0x86, 0xfc, 0x0f, 0xc0, 0xa0, - 0x6e, 0x52, 0x30, 0xb2, 0x60, 0xad, 0x89, 0xa7, 0xef, 0x96, 0x0b, 0x90, 0x67, 0x60, 0x00, 0x2d, - 0x8d, 0x96, 0x5e, 0xf7, 0x23, 0xfe, 0xf1, 0x10, 0x81, 0x3e, 0x50, 0x0e, 0x44, 0x10, 0x50, 0x92, - 0x8f, 0x41, 0x21, 0x92, 0x3f, 0xfe, 0xea, 0x36, 0x7c, 0xf3, 0xae, 0xc8, 0xd1, 0x6a, 0xf9, 0xde, - 0x21, 0x9e, 0x3b, 0x5e, 0x30, 0x25, 0x8b, 0x70, 0x7c, 0xc1, 0xa1, 0x06, 0x7a, 0x81, 0x4e, 0xde, - 0x6f, 0x39, 0x22, 0x96, 0x30, 0x1f, 0xc0, 0xb8, 0x74, 0xb4, 0x7c, 0x34, 0x5b, 0xd4, 0x04, 0x5e, - 0x0e, 0x3b, 0x96, 0x52, 0x9c, 0xd9, 0x13, 0xbc, 0x25, 0x37, 0xe9, 0xfa, 0x1a, 0xe6, 0x10, 0xed, - 0x0f, 0xed, 0x09, 0x21, 0xe8, 0xbb, 0x02, 0x25, 0xdb, 0x13, 0xd1, 0x42, 0x67, 0x9f, 0x87, 0xc1, - 0xdd, 0x46, 0x7c, 0xfd, 0xc5, 0x6c, 0x07, 0xa7, 0xed, 0x07, 0x37, 0xe9, 0x46, 0x90, 0xee, 0xad, - 0xb7, 0x43, 0xba, 0xb7, 0x6f, 0x66, 0x3b, 0x78, 0xa4, 0x3f, 0xd0, 0x69, 0x99, 0x02, 0x61, 0x44, - 0xd3, 0x32, 0x85, 0x19, 0xb1, 0x4c, 0x43, 0x93, 0x89, 0x62, 0x09, 0xdc, 0x0a, 0x5b, 0x26, 0x70, - 0xfb, 0x99, 0x5c, 0x37, 0x8f, 0xfd, 0x23, 0xd9, 0xef, 0x44, 0xf6, 0xd7, 0x60, 0x30, 0x90, 0x6c, - 0xa5, 0x8c, 0xf6, 0xcc, 0x70, 0x10, 0x5f, 0x9a, 0x83, 0xb1, 0x8c, 0x44, 0x44, 0x2e, 0xf3, 0xb6, - 0x56, 0xcd, 0xb7, 0x78, 0xb8, 0xd4, 0x61, 0x11, 0x08, 0x53, 0xf7, 0xf4, 0x9a, 0x6b, 0xbe, 0x45, - 0xb5, 0x00, 0xad, 0xfe, 0xfd, 0x6c, 0xea, 0xb3, 0x87, 0xa3, 0x3e, 0xda, 0x41, 0x1f, 0xa5, 0x08, - 0x91, 0x3f, 0xd8, 0x38, 0x12, 0xe2, 0x0e, 0x84, 0xf8, 0x47, 0xd9, 0xd4, 0xe7, 0x2d, 0x47, 0x42, - 0xdc, 0xc9, 0x6c, 0xf1, 0x04, 0x0c, 0x68, 0xf6, 0x9a, 0x8b, 0xd9, 0x9a, 0xc5, 0x5c, 0x81, 0x13, - 0xb5, 0x63, 0xaf, 0xb9, 0x3c, 0x93, 0xb5, 0x16, 0x12, 0xa8, 0x7f, 0x92, 0xed, 0xf2, 0x00, 0xe8, - 0x48, 0xf0, 0xef, 0xe4, 0x12, 0xf9, 0xcb, 0xd9, 0xc8, 0x03, 0xa3, 0x07, 0x3a, 0xbf, 0x69, 0xb5, - 0xbe, 0x4a, 0x9b, 0x7a, 0x3c, 0xbf, 0xa9, 0x8b, 0x50, 0x91, 0x65, 0x2c, 0x24, 0x51, 0xbf, 0x9c, - 0x8d, 0xbd, 0xb0, 0x3a, 0x92, 0xdd, 0xb6, 0x65, 0x17, 0x68, 0x9d, 0x78, 0x34, 0x76, 0x24, 0xb9, - 0xed, 0x4a, 0xee, 0x53, 0xd9, 0xd8, 0xfb, 0xba, 0x07, 0x56, 0x76, 0x6c, 0x00, 0x26, 0xdf, 0xfd, - 0x3d, 0xb0, 0x9a, 0xf4, 0x04, 0x0c, 0x08, 0x39, 0x04, 0x4b, 0x05, 0x9f, 0xf7, 0x39, 0x10, 0x0f, - 0x50, 0x03, 0x02, 0xf5, 0xbb, 0xb2, 0x10, 0x7d, 0xf7, 0xf8, 0x80, 0xea, 0xd0, 0x2f, 0x67, 0xa3, - 0x2f, 0x3e, 0x1f, 0x5c, 0xfd, 0xb9, 0x02, 0x50, 0x6d, 0x2f, 0xd5, 0x45, 0xc0, 0xc0, 0x5e, 0xe9, - 0x04, 0x3e, 0x80, 0x6a, 0x12, 0x85, 0xfa, 0x3f, 0xb3, 0xa9, 0xcf, 0x50, 0x1f, 0x5c, 0x01, 0x3e, - 0x8d, 0xa7, 0xe2, 0x75, 0x2b, 0x9c, 0xc8, 0xf1, 0x10, 0x92, 0x8d, 0xbf, 0x44, 0x9e, 0x12, 0x9f, - 0x90, 0x7c, 0x20, 0xc5, 0x5c, 0xc3, 0x28, 0xaa, 0xa1, 0xb9, 0x26, 0xdf, 0x30, 0x48, 0x86, 0xdb, - 0x3f, 0xc9, 0x6e, 0xf5, 0x6a, 0xf7, 0x41, 0x5e, 0x55, 0xfb, 0x16, 0xf4, 0x75, 0x8c, 0x2e, 0xc5, - 0x7a, 0x62, 0x88, 0x67, 0xd1, 0x68, 0x71, 0x90, 0x7c, 0x23, 0x26, 0xa8, 0xd4, 0x3f, 0xec, 0x4d, - 0x7f, 0x32, 0xfa, 0xe0, 0x8a, 0xf0, 0x1c, 0xe4, 0x17, 0x74, 0x6f, 0x55, 0x68, 0x32, 0xde, 0xd6, - 0xb5, 0x74, 0x6f, 0x55, 0x43, 0x28, 0xb9, 0x0c, 0xfd, 0x9a, 0xbe, 0xc6, 0xcf, 0x3c, 0x0b, 0x61, - 0x86, 0x13, 0x47, 0x5f, 0xab, 0xf1, 0x73, 0xcf, 0x00, 0x4d, 0xd4, 0x20, 0xc3, 0x0e, 0x3f, 0xf9, - 0xc6, 0xf4, 0x0e, 0x3c, 0xc3, 0x4e, 0x90, 0x57, 0xe7, 0x1c, 0xe4, 0xc7, 0x6d, 0x63, 0x1d, 0x9d, - 0x59, 0x86, 0x78, 0x65, 0x4b, 0xb6, 0xb1, 0xae, 0x21, 0x94, 0x7c, 0x3a, 0x03, 0x7d, 0xd3, 0x54, - 0x37, 0xd8, 0x08, 0x19, 0xe8, 0xe6, 0x0b, 0xf2, 0xa1, 0xfd, 0xf1, 0x05, 0x19, 0x5b, 0xe5, 0x95, - 0xc9, 0x8a, 0x22, 0xea, 0x27, 0x37, 0xa0, 0x7f, 0x42, 0xf7, 0xe8, 0x8a, 0xed, 0xac, 0xa3, 0x77, - 0xcb, 0x48, 0xe8, 0x76, 0x18, 0xd1, 0x1f, 0x9f, 0x88, 0xdf, 0x8c, 0xd5, 0xc5, 0x2f, 0x2d, 0x28, - 0xcc, 0xc4, 0x22, 0xd2, 0x75, 0x0e, 0x86, 0x62, 0xe1, 0x79, 0x39, 0x83, 0xac, 0x9c, 0xc1, 0xb1, - 0xf2, 0x50, 0xfa, 0xb1, 0x32, 0x5a, 0x8f, 0xe8, 0x01, 0x87, 0x79, 0x6d, 0x86, 0x71, 0xd1, 0xe7, - 0xd6, 0x23, 0x42, 0x31, 0xad, 0x8d, 0x26, 0x91, 0xa8, 0x5f, 0xef, 0x85, 0xd4, 0x07, 0x66, 0x47, - 0x4a, 0x7e, 0xa4, 0xe4, 0xa1, 0x92, 0x97, 0x13, 0x4a, 0x7e, 0x36, 0xf9, 0x64, 0xf1, 0x5d, 0xaa, - 0xe1, 0x5f, 0xcc, 0x27, 0x1e, 0x3c, 0x3f, 0xd8, 0xbb, 0xcb, 0x50, 0x7a, 0xbd, 0x5b, 0x4a, 0x2f, - 0x18, 0x10, 0x85, 0x2d, 0x07, 0x44, 0xdf, 0x76, 0x07, 0x44, 0x7f, 0xc7, 0x01, 0x11, 0x2a, 0xc8, - 0x40, 0x47, 0x05, 0xa9, 0x88, 0x41, 0x03, 0xdd, 0x73, 0xa6, 0x9d, 0xdb, 0xdc, 0x28, 0x8e, 0xb0, - 0xd1, 0x94, 0x9a, 0x2d, 0x0d, 0x59, 0xa8, 0x5f, 0xcb, 0x77, 0x89, 0x52, 0x70, 0x20, 0x3a, 0xf2, - 0x34, 0xe4, 0x4a, 0xad, 0x96, 0xd0, 0x8f, 0xe3, 0x52, 0x80, 0x84, 0x0e, 0xa5, 0x18, 0x35, 0x79, - 0x01, 0x72, 0xa5, 0x3b, 0xd5, 0x78, 0xac, 0xf5, 0xd2, 0x9d, 0xaa, 0xf8, 0x92, 0x8e, 0x65, 0xef, - 0x54, 0xc9, 0x4b, 0x61, 0xd0, 0xb3, 0xd5, 0xb6, 0x75, 0x57, 0x6c, 0x14, 0x85, 0x13, 0xac, 0xef, - 0x69, 0x53, 0x67, 0x28, 0xb6, 0x5d, 0x8c, 0xd1, 0xc6, 0xb4, 0xa9, 0xb0, 0x7d, 0x6d, 0xea, 0xdb, - 0x52, 0x9b, 0xfa, 0xb7, 0xab, 0x4d, 0x03, 0xdb, 0xd0, 0x26, 0xd8, 0x52, 0x9b, 0x06, 0xf7, 0xae, - 0x4d, 0x2d, 0x38, 0x9b, 0x8c, 0x2c, 0x13, 0x68, 0x84, 0x06, 0x24, 0x89, 0x15, 0x8e, 0x25, 0x78, - 0xf5, 0xdf, 0xe6, 0xd8, 0x1a, 0x4f, 0xab, 0x1b, 0x4f, 0x4a, 0xab, 0xa5, 0x94, 0x56, 0x7f, 0x31, - 0xdb, 0x39, 0x20, 0xce, 0xe1, 0x9c, 0xe2, 0xbe, 0x2d, 0x55, 0x4a, 0xf9, 0xe8, 0x03, 0xc5, 0xce, - 0x52, 0x8e, 0xb1, 0x4d, 0x93, 0xd9, 0x57, 0x33, 0x9d, 0xa2, 0xf4, 0xec, 0x49, 0x62, 0x8f, 0x25, - 0x9d, 0xd5, 0xd0, 0x7b, 0xde, 0x8d, 0x7a, 0xa9, 0xc5, 0xb3, 0xb4, 0xe6, 0x76, 0x99, 0xa5, 0xf5, - 0xd7, 0x32, 0x70, 0xfc, 0x66, 0x7b, 0x89, 0x0a, 0xe7, 0xb4, 0xa0, 0x19, 0x6f, 0x02, 0x30, 0xb0, - 0x70, 0x62, 0xc9, 0xa0, 0x13, 0xcb, 0xfb, 0xe4, 0x08, 0x3b, 0xb1, 0x02, 0x57, 0x42, 0x6a, 0xee, - 0xc0, 0x72, 0xde, 0x77, 0xb1, 0xbc, 0xdb, 0x5e, 0xa2, 0xb5, 0x84, 0x27, 0x8b, 0xc4, 0xfd, 0xec, - 0xcb, 0xdc, 0x79, 0x7d, 0xb7, 0x4e, 0x23, 0x3f, 0x9f, 0xed, 0x18, 0xd4, 0xe8, 0xd0, 0xe6, 0x94, - 0xf9, 0x48, 0x6a, 0xaf, 0xc4, 0x73, 0xcb, 0xa4, 0x90, 0xc4, 0x38, 0xa6, 0x71, 0x49, 0x17, 0xd8, - 0x21, 0xcf, 0x74, 0xf4, 0x8e, 0x0a, 0xec, 0xb7, 0x33, 0x1d, 0x83, 0x4f, 0x1d, 0x56, 0x81, 0xa9, - 0xff, 0x2e, 0xe7, 0xc7, 0xbc, 0xda, 0xd3, 0x27, 0x3c, 0x01, 0x03, 0xe2, 0xe9, 0x5f, 0xd4, 0xb7, - 0x56, 0x1c, 0xe5, 0xe1, 0xd1, 0x70, 0x40, 0xc0, 0x96, 0x79, 0x3f, 0x26, 0x4f, 0x90, 0xa2, 0x17, - 0x97, 0x79, 0x53, 0x40, 0x19, 0xbd, 0x44, 0xc2, 0x16, 0xf2, 0xc9, 0xfb, 0xa6, 0x87, 0x56, 0x01, - 0xeb, 0xcb, 0x1c, 0x5f, 0xc8, 0xe9, 0x7d, 0xd3, 0xe3, 0x36, 0x41, 0x80, 0x66, 0x8b, 0x74, 0x35, - 0xcc, 0xe3, 0x28, 0x16, 0x69, 0x57, 0xa4, 0xb3, 0x14, 0x8f, 0xb9, 0x9e, 0x80, 0x01, 0xe1, 0xb0, - 0x2a, 0xdc, 0x4c, 0x44, 0x6b, 0x85, 0x8b, 0x2b, 0xb6, 0x36, 0x20, 0x60, 0x1c, 0x35, 0xba, 0x12, - 0x3a, 0xd6, 0x21, 0x47, 0x07, 0x21, 0x9a, 0xc0, 0x90, 0x6b, 0x30, 0x52, 0xf5, 0x74, 0xcb, 0xd0, - 0x1d, 0x63, 0xbe, 0xed, 0xb5, 0xda, 0x9e, 0x6c, 0x94, 0xba, 0x9e, 0x61, 0xb7, 0x3d, 0x2d, 0x46, - 0x41, 0xde, 0x0f, 0xc3, 0x3e, 0x64, 0xd2, 0x71, 0x6c, 0x47, 0xb6, 0x3c, 0x5c, 0xcf, 0xa0, 0x8e, - 0xa3, 0x45, 0x09, 0xc8, 0x07, 0x60, 0xb8, 0x62, 0xdd, 0xb3, 0x79, 0x82, 0xd0, 0x5b, 0xda, 0x8c, - 0xb0, 0x43, 0xf0, 0x81, 0x94, 0x19, 0x20, 0x6a, 0x6d, 0xa7, 0xa1, 0x45, 0x09, 0xd5, 0xcd, 0x6c, - 0x32, 0x34, 0xd8, 0x83, 0xbb, 0x69, 0xb9, 0x1c, 0x75, 0xa6, 0x43, 0x0f, 0x52, 0x34, 0x08, 0x65, - 0x5f, 0x5e, 0x6e, 0x17, 0x5e, 0x83, 0xfe, 0x9b, 0x74, 0x9d, 0xfb, 0x7d, 0x16, 0x42, 0x57, 0xe1, - 0xbb, 0x02, 0x26, 0x9f, 0xb8, 0xfa, 0x74, 0xea, 0x57, 0xb2, 0xc9, 0xa0, 0x67, 0x0f, 0xae, 0xb0, - 0xdf, 0x0f, 0x7d, 0x28, 0xca, 0x8a, 0x7f, 0xe4, 0x8f, 0x02, 0x44, 0x71, 0x47, 0x3d, 0x90, 0x7d, - 0x32, 0xf5, 0xc7, 0x0b, 0xf1, 0x48, 0x78, 0x0f, 0xae, 0xf4, 0x5e, 0x84, 0xc1, 0x09, 0xdb, 0x72, - 0x4d, 0xd7, 0xa3, 0x56, 0xdd, 0x57, 0xd8, 0x33, 0xcc, 0xa0, 0xaa, 0x87, 0x60, 0xf9, 0x65, 0x90, - 0x44, 0xbd, 0x1b, 0xe5, 0x25, 0xcf, 0xc2, 0x00, 0x8a, 0x1c, 0xfd, 0xa4, 0xa5, 0x04, 0xe4, 0x4b, - 0x0c, 0x18, 0x77, 0x92, 0x0e, 0x49, 0xc9, 0x2d, 0xe8, 0x9f, 0x58, 0x35, 0x1b, 0x86, 0x43, 0x2d, - 0xf4, 0x17, 0x96, 0x1e, 0x1c, 0x47, 0xfb, 0xf2, 0x0a, 0xfe, 0x8b, 0xb4, 0xbc, 0x39, 0x75, 0x51, - 0x2c, 0xf2, 0x36, 0x4a, 0xc0, 0xce, 0xfe, 0x40, 0x16, 0x20, 0x2c, 0x40, 0x1e, 0x86, 0x6c, 0x90, - 0x22, 0x0d, 0xdd, 0x54, 0x22, 0x1a, 0x94, 0xc5, 0xa5, 0x42, 0x8c, 0xed, 0xec, 0x96, 0x63, 0xfb, - 0x16, 0x14, 0xf8, 0x89, 0x17, 0x7a, 0x92, 0x4b, 0xc1, 0xb9, 0x3a, 0x36, 0xf8, 0x0a, 0xd2, 0xf3, - 0xcd, 0x2c, 0x5a, 0x9e, 0x11, 0xaf, 0x6c, 0xce, 0xec, 0x6c, 0x1d, 0x7a, 0xf1, 0x2f, 0x72, 0x11, - 0xf2, 0x28, 0xc5, 0x0c, 0xee, 0x63, 0x71, 0x96, 0x8e, 0xc9, 0x0f, 0xf1, 0xac, 0x9b, 0x26, 0x6c, - 0xcb, 0x63, 0x55, 0x63, 0xab, 0x87, 0x84, 0x5c, 0x04, 0x2c, 0x22, 0x17, 0x01, 0x53, 0xff, 0x71, - 0x36, 0x25, 0x46, 0xe3, 0x83, 0x3b, 0x4c, 0x9e, 0x07, 0xc0, 0x87, 0xd6, 0x4c, 0x9e, 0xfe, 0x13, - 0x0d, 0x1c, 0x25, 0xc8, 0x08, 0xd5, 0x36, 0xb2, 0xed, 0x08, 0x89, 0xd5, 0xdf, 0xc8, 0x24, 0x02, - 0xfb, 0xed, 0x49, 0x8e, 0xb2, 0x55, 0x96, 0xdd, 0xa5, 0x19, 0xeb, 0xf7, 0x45, 0x6e, 0x67, 0x7d, - 0x11, 0xfd, 0x96, 0x7d, 0xb0, 0x4c, 0x0f, 0xf2, 0x5b, 0xbe, 0x9e, 0x4d, 0x0b, 0x73, 0x78, 0x38, - 0x55, 0xfc, 0x7a, 0x60, 0x94, 0xe6, 0xb7, 0x93, 0x5c, 0x5c, 0x98, 0xa9, 0x1f, 0x87, 0x63, 0xb1, - 0xe0, 0x7f, 0x22, 0x6f, 0xe1, 0xc5, 0xee, 0x51, 0x04, 0x3b, 0x3f, 0xd1, 0x8f, 0x90, 0xa9, 0xff, - 0x2b, 0xd3, 0x3d, 0xf4, 0xe3, 0x81, 0xab, 0x4e, 0x8a, 0x00, 0x72, 0x7f, 0x3e, 0x02, 0xd8, 0x87, - 0x6d, 0xf0, 0xe1, 0x16, 0xc0, 0xbb, 0x64, 0xf2, 0x78, 0xa7, 0x05, 0xf0, 0xe3, 0x99, 0x2d, 0x23, - 0x77, 0x1e, 0xb4, 0x0c, 0xd4, 0x7f, 0x9d, 0x49, 0x8d, 0xb0, 0xb9, 0xa7, 0x76, 0xbd, 0x04, 0x05, - 0xee, 0x56, 0x23, 0x5a, 0x25, 0xe5, 0x24, 0x61, 0xd0, 0x0e, 0xe5, 0x45, 0x19, 0x32, 0x03, 0x7d, - 0xbc, 0x0d, 0x46, 0x3c, 0x77, 0x6f, 0x4a, 0x3b, 0x8d, 0x4e, 0x93, 0xa3, 0x40, 0xab, 0xbf, 0x9e, - 0x49, 0x04, 0xfc, 0x3c, 0xc0, 0x6f, 0x0b, 0xa7, 0xea, 0xdc, 0xf6, 0xa7, 0x6a, 0xf5, 0x0f, 0xb2, - 0xe9, 0xf1, 0x46, 0x0f, 0xf0, 0x43, 0xf6, 0xe3, 0x38, 0x6d, 0x77, 0xeb, 0xd6, 0x22, 0x8c, 0x44, - 0x65, 0x21, 0x96, 0xad, 0x0b, 0xe9, 0x51, 0x57, 0x3b, 0xb4, 0x22, 0xc6, 0x43, 0x7d, 0x3b, 0x93, - 0x0c, 0x95, 0x7a, 0xe0, 0xf3, 0xd3, 0xee, 0xb4, 0x25, 0xfa, 0x29, 0xef, 0x92, 0xb5, 0x66, 0x3f, - 0x3e, 0xe5, 0x5d, 0xb2, 0x6a, 0xec, 0xee, 0x53, 0x7e, 0x36, 0xdb, 0x29, 0xd2, 0xec, 0x81, 0x7f, - 0xd0, 0x87, 0x65, 0x21, 0xf3, 0x96, 0x89, 0x4f, 0x7b, 0xb8, 0x53, 0x68, 0xd7, 0x0e, 0x3c, 0x13, - 0x7c, 0x76, 0x37, 0xc6, 0x53, 0x85, 0xf5, 0x2e, 0x51, 0xe4, 0xc3, 0x21, 0xac, 0x77, 0xc9, 0x50, - 0x79, 0xf7, 0x09, 0xeb, 0xef, 0x64, 0xb7, 0x1b, 0xde, 0xf8, 0x48, 0x78, 0x09, 0xe1, 0x7d, 0x3e, - 0x9b, 0x0c, 0xbb, 0x7d, 0xe0, 0x62, 0x9a, 0x82, 0x82, 0x08, 0x00, 0xde, 0x51, 0x38, 0x1c, 0xdf, - 0xc9, 0xa2, 0x11, 0xdf, 0x71, 0x1d, 0xc4, 0x45, 0xce, 0xf6, 0x44, 0xc2, 0x69, 0xd5, 0x3f, 0xc9, - 0xc4, 0x62, 0x54, 0x1f, 0xc8, 0x11, 0xc2, 0xae, 0x96, 0x24, 0xf2, 0xb2, 0x7f, 0x98, 0x99, 0x8f, - 0xc5, 0x08, 0x0d, 0xbe, 0xa7, 0x4c, 0x3d, 0xdd, 0x6c, 0xc4, 0xcb, 0x8b, 0x98, 0x00, 0x5f, 0xc9, - 0xc2, 0x58, 0x82, 0x94, 0x5c, 0x8c, 0x44, 0xc9, 0xc1, 0x63, 0xc9, 0x98, 0xf3, 0x38, 0x8f, 0x97, - 0xb3, 0x83, 0x93, 0xd4, 0x8b, 0x90, 0x2f, 0xeb, 0xeb, 0xfc, 0xdb, 0x7a, 0x39, 0x4b, 0x43, 0x5f, - 0x97, 0x4f, 0xdc, 0x10, 0x4f, 0x96, 0xe0, 0x24, 0xbf, 0x0f, 0x31, 0x6d, 0x6b, 0xd1, 0x6c, 0xd2, - 0x8a, 0x35, 0x6b, 0x36, 0x1a, 0xa6, 0x2b, 0x2e, 0xf5, 0x9e, 0xd8, 0xdc, 0x28, 0x5e, 0xf2, 0x6c, - 0x4f, 0x6f, 0xd4, 0xa8, 0x4f, 0x56, 0xf3, 0xcc, 0x26, 0xad, 0x99, 0x56, 0xad, 0x89, 0x94, 0x12, - 0xcb, 0x74, 0x56, 0xa4, 0xc2, 0xc3, 0xc1, 0x56, 0xeb, 0xba, 0x65, 0x51, 0xa3, 0x62, 0x8d, 0xaf, - 0x7b, 0x94, 0x5f, 0x06, 0xe6, 0xf8, 0x91, 0x20, 0x7f, 0x1b, 0xce, 0xd1, 0x8c, 0xf1, 0x12, 0x23, - 0xd0, 0x52, 0x0a, 0xa9, 0xbf, 0x9a, 0x4f, 0x09, 0x4f, 0x7e, 0x88, 0xd4, 0xc7, 0xef, 0xe9, 0xfc, - 0x16, 0x3d, 0x7d, 0x15, 0xfa, 0x6e, 0x53, 0x07, 0xcf, 0xb7, 0xf8, 0x05, 0x03, 0x3a, 0xb3, 0xdf, - 0xe3, 0x20, 0xf9, 0x86, 0x46, 0x50, 0x91, 0x06, 0x9c, 0x5d, 0x64, 0xdd, 0x94, 0xde, 0x99, 0x85, - 0x5d, 0x74, 0x66, 0x17, 0x7e, 0xe4, 0x0d, 0x38, 0x8d, 0xd8, 0x94, 0x6e, 0xed, 0xc3, 0xaa, 0x30, - 0x72, 0x14, 0xaf, 0x2a, 0xbd, 0x73, 0x3b, 0x95, 0x27, 0x1f, 0x86, 0xa1, 0x60, 0x80, 0x98, 0xd4, - 0x15, 0x37, 0x17, 0x5d, 0xc6, 0x19, 0x0f, 0xcb, 0xc6, 0xc0, 0xe8, 0x42, 0x16, 0x0d, 0xed, 0x15, - 0xe1, 0xa5, 0xfe, 0xab, 0x4c, 0xb7, 0x30, 0xe9, 0x07, 0x3e, 0x2b, 0xbf, 0x0c, 0x7d, 0x06, 0xff, - 0x28, 0xa1, 0x53, 0xdd, 0x03, 0xa9, 0x73, 0x52, 0xcd, 0x2f, 0xa3, 0xfe, 0x7e, 0xa6, 0x6b, 0x74, - 0xf6, 0xc3, 0xfe, 0x79, 0x9f, 0xcf, 0x75, 0xf8, 0x3c, 0x31, 0x89, 0x5e, 0x86, 0x51, 0x33, 0x0c, - 0x1f, 0x5b, 0x0b, 0xc3, 0x4f, 0x69, 0xc7, 0x24, 0x38, 0x8e, 0xae, 0xeb, 0x70, 0xca, 0x77, 0x7c, - 0x74, 0x7c, 0x0f, 0x31, 0xb7, 0xd6, 0x76, 0x4c, 0x3e, 0x2e, 0xb5, 0x13, 0x6e, 0xcc, 0x7d, 0xcc, - 0xbd, 0xe5, 0x98, 0xac, 0x02, 0xdd, 0x5b, 0xa5, 0x96, 0x5e, 0x5b, 0xb3, 0x9d, 0xbb, 0x18, 0xfb, - 0x93, 0x0f, 0x4e, 0xed, 0x18, 0x87, 0xdf, 0xf1, 0xc1, 0xe4, 0x51, 0x18, 0x5e, 0x69, 0xb4, 0x69, - 0x10, 0x6d, 0x91, 0xdf, 0xf5, 0x69, 0x43, 0x0c, 0x18, 0xdc, 0x90, 0x9c, 0x07, 0x40, 0x22, 0x0f, - 0x63, 0xe7, 0xe3, 0xc5, 0x9e, 0x36, 0xc0, 0x20, 0x8b, 0xa2, 0xbb, 0xce, 0x72, 0xad, 0xe6, 0x42, - 0xaa, 0x35, 0x6c, 0x6b, 0xa5, 0xe6, 0x51, 0xa7, 0x89, 0x0d, 0x45, 0x67, 0x06, 0xed, 0x14, 0x52, - 0xe0, 0xd5, 0x89, 0x3b, 0x63, 0x5b, 0x2b, 0x8b, 0xd4, 0x69, 0xb2, 0xa6, 0x3e, 0x01, 0x44, 0x34, - 0xd5, 0xc1, 0x43, 0x0f, 0xfe, 0x71, 0xe8, 0xcd, 0xa0, 0x89, 0x8f, 0xe0, 0xa7, 0x21, 0xf8, 0x61, - 0x45, 0x18, 0xe4, 0x21, 0xe7, 0xb8, 0xd0, 0xd0, 0x85, 0x41, 0x03, 0x0e, 0x42, 0x79, 0x9d, 0x02, - 0xe1, 0x5d, 0xc1, 0xbd, 0xba, 0x35, 0xf1, 0x4b, 0xfd, 0x4c, 0x2e, 0x2d, 0xa0, 0xfc, 0x9e, 0x14, - 0x2d, 0x9c, 0x56, 0xb3, 0x3b, 0x9a, 0x56, 0x8f, 0x59, 0xed, 0x66, 0x4d, 0x6f, 0xb5, 0x6a, 0xcb, - 0x66, 0x03, 0x9f, 0x55, 0xe1, 0xc2, 0xa7, 0x0d, 0x5b, 0xed, 0x66, 0xa9, 0xd5, 0x9a, 0xe2, 0x40, - 0xf2, 0x38, 0x8c, 0x31, 0x3a, 0xec, 0xa4, 0x80, 0x32, 0x8f, 0x94, 0x8c, 0x01, 0xc6, 0x6c, 0xf5, - 0x69, 0xcf, 0x40, 0xbf, 0xe0, 0xc9, 0xd7, 0xaa, 0x5e, 0xad, 0x8f, 0x33, 0x73, 0x59, 0xcf, 0x05, - 0x6c, 0xf8, 0xe4, 0xda, 0xab, 0x0d, 0xf8, 0xe5, 0x31, 0x32, 0xb1, 0xd5, 0x6e, 0xf2, 0x88, 0x58, - 0x7d, 0x88, 0x0c, 0x7e, 0x93, 0x8b, 0x30, 0xc2, 0xb8, 0x04, 0x02, 0xe3, 0xc1, 0x5c, 0x7b, 0xb5, - 0x18, 0x94, 0x5c, 0x83, 0x13, 0x11, 0x08, 0xb7, 0x41, 0xf9, 0x33, 0x81, 0x5e, 0x2d, 0x15, 0xa7, - 0x7e, 0x39, 0x17, 0x0d, 0x73, 0x7f, 0x00, 0x1d, 0x71, 0x1a, 0xfa, 0x6c, 0x67, 0xa5, 0xd6, 0x76, - 0x1a, 0x62, 0xec, 0x15, 0x6c, 0x67, 0xe5, 0x96, 0xd3, 0x20, 0x27, 0xa1, 0xc0, 0x7a, 0xc7, 0x34, - 0xc4, 0x10, 0xeb, 0xd5, 0x5b, 0xad, 0x8a, 0x41, 0x4a, 0xbc, 0x43, 0x30, 0x10, 0x68, 0xad, 0x8e, - 0x5b, 0x7b, 0xee, 0x94, 0xd0, 0xcb, 0x57, 0xbc, 0x04, 0x12, 0xfb, 0x09, 0xc3, 0x83, 0xf2, 0x83, - 0x80, 0x18, 0x0b, 0x03, 0xb7, 0x25, 0x06, 0xef, 0x93, 0x38, 0x0b, 0x81, 0x0c, 0x59, 0xf0, 0x4d, - 0x8c, 0x41, 0xca, 0x40, 0x42, 0xaa, 0xa6, 0x6d, 0x98, 0xcb, 0x26, 0xe5, 0xaf, 0x3a, 0x7a, 0xf9, - 0xc5, 0x6f, 0x12, 0xab, 0x8d, 0xfa, 0x4c, 0x66, 0x05, 0x84, 0xbc, 0xc8, 0x95, 0x90, 0xd3, 0xe1, - 0xda, 0xc7, 0xfb, 0x96, 0xdb, 0x69, 0x31, 0x14, 0x6a, 0x26, 0x96, 0xc7, 0x85, 0x50, 0x7d, 0x3b, - 0x97, 0xcc, 0x75, 0x70, 0x20, 0x76, 0xcd, 0x34, 0x80, 0x48, 0x65, 0x12, 0x5e, 0xae, 0x05, 0x1e, - 0xe7, 0x21, 0xa6, 0x03, 0x0f, 0xa9, 0x2c, 0xb9, 0x0c, 0xfd, 0xfc, 0x8b, 0x2a, 0x65, 0x61, 0xef, - 0xa0, 0x8b, 0x98, 0xdb, 0x32, 0x97, 0x97, 0xd1, 0x9f, 0x2c, 0x40, 0x93, 0x8b, 0xd0, 0x57, 0x9e, - 0xab, 0x56, 0x4b, 0x73, 0xfe, 0x4d, 0x31, 0xbe, 0x2f, 0x31, 0x2c, 0xb7, 0xe6, 0xea, 0x96, 0xab, - 0xf9, 0x48, 0xf2, 0x28, 0x14, 0x2a, 0x0b, 0x48, 0xc6, 0x5f, 0x4d, 0x0e, 0x6e, 0x6e, 0x14, 0xfb, - 0xcc, 0x16, 0xa7, 0x12, 0x28, 0xac, 0xf7, 0x76, 0xa5, 0x2c, 0xb9, 0x4b, 0xf0, 0x7a, 0xef, 0x99, - 0x06, 0x5e, 0x3b, 0x6b, 0x01, 0x9a, 0x3c, 0x03, 0x43, 0x55, 0xea, 0x98, 0x7a, 0x63, 0xae, 0x8d, - 0x5b, 0x45, 0xee, 0x22, 0x36, 0xb6, 0xb9, 0x51, 0x1c, 0x76, 0x11, 0x5e, 0xb3, 0x10, 0xa1, 0x45, - 0xc8, 0xc8, 0x39, 0xc8, 0x4f, 0x9b, 0x96, 0xff, 0x84, 0x01, 0x7d, 0xdc, 0x57, 0x4d, 0xcb, 0xd3, - 0x10, 0xaa, 0xfe, 0x97, 0x6c, 0x7a, 0xc2, 0x88, 0x03, 0x18, 0x8e, 0xbb, 0xbc, 0xe9, 0x8d, 0x29, - 0x41, 0x7e, 0x0f, 0x4a, 0xb0, 0x0c, 0xc7, 0x4a, 0x46, 0xd3, 0xb4, 0x4a, 0xf8, 0xd3, 0x9d, 0x9d, - 0x2a, 0xe1, 0xf0, 0x96, 0x9e, 0xd0, 0xc5, 0xd0, 0xe2, 0x7b, 0x78, 0xa8, 0x5c, 0x86, 0xaa, 0xe9, - 0x1c, 0x57, 0x6b, 0x2e, 0xeb, 0xb5, 0x3a, 0xcf, 0xb5, 0xa0, 0xc5, 0x99, 0xaa, 0xdf, 0x9f, 0xdd, - 0x22, 0xc7, 0xc5, 0x83, 0x28, 0x7d, 0xf5, 0x0b, 0xd9, 0xee, 0x69, 0x46, 0x1e, 0x48, 0xa1, 0xfc, - 0x51, 0x36, 0x25, 0xe9, 0xc7, 0x9e, 0x24, 0x71, 0x19, 0xfa, 0x39, 0x9b, 0xc0, 0xd5, 0x16, 0x67, - 0x1c, 0xae, 0xac, 0x38, 0xd3, 0xf9, 0x68, 0x32, 0x07, 0x27, 0x4a, 0xcb, 0xcb, 0xb4, 0xee, 0x85, - 0x41, 0x93, 0xe7, 0xc2, 0x40, 0xa9, 0x3c, 0xd2, 0xac, 0xc0, 0x87, 0x41, 0x97, 0x31, 0x20, 0x48, - 0x6a, 0x39, 0xb2, 0x08, 0xa7, 0xe2, 0xf0, 0x2a, 0x37, 0xd3, 0xf3, 0x52, 0xf0, 0xd9, 0x04, 0x47, - 0xfe, 0x9f, 0xd6, 0xa1, 0x6c, 0x5a, 0x2b, 0x71, 0x3a, 0xed, 0xed, 0xd6, 0x4a, 0x9c, 0x5b, 0x53, - 0xcb, 0xa9, 0x5f, 0xc9, 0xc9, 0xb9, 0x51, 0x1e, 0x5c, 0xa7, 0xa8, 0xeb, 0x11, 0x57, 0xe8, 0xed, - 0x0e, 0x99, 0x67, 0x44, 0x94, 0x0f, 0xa3, 0xed, 0xf8, 0x5e, 0x83, 0x41, 0x94, 0x01, 0x04, 0xca, - 0xfe, 0x7f, 0x01, 0x25, 0xa9, 0x40, 0xbe, 0xe4, 0xac, 0x70, 0x13, 0x74, 0xab, 0x87, 0x4f, 0xba, - 0xb3, 0xe2, 0xa6, 0x3f, 0x7c, 0x62, 0x2c, 0xd4, 0xef, 0xcb, 0x76, 0x49, 0x67, 0xf2, 0x20, 0x4e, - 0x22, 0x8f, 0xcf, 0xf2, 0x20, 0xc7, 0x37, 0x4d, 0xcb, 0x20, 0x67, 0xe0, 0xe4, 0xad, 0xea, 0xa4, - 0x56, 0xbb, 0x59, 0x99, 0x2b, 0xd7, 0x6e, 0xcd, 0x55, 0x17, 0x26, 0x27, 0x2a, 0x53, 0x95, 0xc9, - 0xf2, 0x68, 0x0f, 0x39, 0x0e, 0xc7, 0x42, 0xd4, 0xf4, 0xad, 0xd9, 0xd2, 0xdc, 0x68, 0x86, 0x8c, - 0xc1, 0x70, 0x08, 0x1c, 0x9f, 0x5f, 0x1c, 0xcd, 0x3e, 0xfe, 0x5e, 0x18, 0xc4, 0x5d, 0x1c, 0x5f, - 0xd1, 0xc8, 0x10, 0xf4, 0xcf, 0x8f, 0x57, 0x27, 0xb5, 0xdb, 0xc8, 0x04, 0xa0, 0x50, 0x9e, 0x9c, - 0x63, 0x0c, 0x33, 0x8f, 0xff, 0xf7, 0x0c, 0x40, 0x75, 0x6a, 0x71, 0x41, 0x10, 0x0e, 0x42, 0x5f, - 0x65, 0xee, 0x76, 0x69, 0xa6, 0xc2, 0xe8, 0xfa, 0x21, 0x3f, 0xbf, 0x30, 0xc9, 0x6a, 0x18, 0x80, - 0xde, 0x89, 0x99, 0xf9, 0xea, 0xe4, 0x68, 0x96, 0x01, 0xb5, 0xc9, 0x52, 0x79, 0x34, 0xc7, 0x80, - 0x77, 0xb4, 0xca, 0xe2, 0xe4, 0x68, 0x9e, 0xfd, 0x39, 0x53, 0x5d, 0x2c, 0x2d, 0x8e, 0xf6, 0xb2, - 0x3f, 0xa7, 0xf0, 0xcf, 0x02, 0x63, 0x56, 0x9d, 0x5c, 0xc4, 0x1f, 0x7d, 0xac, 0x09, 0x53, 0xfe, - 0xaf, 0x7e, 0x86, 0x62, 0xac, 0xcb, 0x15, 0x6d, 0x74, 0x80, 0xfd, 0x60, 0x2c, 0xd9, 0x0f, 0x60, - 0x8d, 0xd3, 0x26, 0x67, 0xe7, 0x6f, 0x4f, 0x8e, 0x0e, 0x32, 0x5e, 0xb3, 0x37, 0x19, 0x78, 0x88, - 0xfd, 0xa9, 0xcd, 0xb2, 0x3f, 0x87, 0x19, 0x27, 0x6d, 0xb2, 0x34, 0xb3, 0x50, 0x5a, 0x9c, 0x1e, - 0x1d, 0x61, 0xed, 0x41, 0x9e, 0xc7, 0x78, 0xc9, 0xb9, 0xd2, 0xec, 0xe4, 0xe8, 0xa8, 0xa0, 0x29, - 0xcf, 0x54, 0xe6, 0x6e, 0x8e, 0x8e, 0x61, 0x43, 0xde, 0x98, 0xc5, 0x1f, 0x84, 0x15, 0xc0, 0xbf, - 0x8e, 0x3f, 0xfe, 0x51, 0x28, 0xcc, 0x57, 0xd1, 0x6e, 0x3b, 0x0d, 0xc7, 0xe7, 0xab, 0xb5, 0xc5, - 0x37, 0x16, 0x26, 0x63, 0xf2, 0x1e, 0x83, 0x61, 0x1f, 0x31, 0x53, 0x99, 0xbb, 0xf5, 0x21, 0x2e, - 0x6d, 0x1f, 0x34, 0x5b, 0x9a, 0x98, 0xaf, 0x8e, 0x66, 0x59, 0xaf, 0xf8, 0xa0, 0x3b, 0x95, 0xb9, - 0xf2, 0xfc, 0x9d, 0xea, 0x68, 0xee, 0xf1, 0x7b, 0x7e, 0xc6, 0xd5, 0x79, 0xc7, 0x5c, 0x31, 0x2d, - 0x72, 0x1e, 0xce, 0x94, 0x27, 0x6f, 0x57, 0x26, 0x26, 0x6b, 0xf3, 0x5a, 0xe5, 0x46, 0x65, 0x2e, - 0x56, 0xd3, 0x49, 0x18, 0x8b, 0xa2, 0x4b, 0x0b, 0x95, 0xd1, 0x0c, 0x39, 0x05, 0x24, 0x0a, 0x7e, - 0xad, 0x34, 0x3b, 0x35, 0x9a, 0x25, 0x0a, 0x9c, 0x88, 0xc2, 0x2b, 0x73, 0x8b, 0xb7, 0xe6, 0x26, - 0x47, 0x73, 0x8f, 0xff, 0x64, 0x06, 0x4e, 0xa6, 0x86, 0x11, 0x20, 0x2a, 0x5c, 0x98, 0x9c, 0x29, - 0x55, 0x17, 0x2b, 0x13, 0xd5, 0xc9, 0x92, 0x36, 0x31, 0x5d, 0x9b, 0x28, 0x2d, 0x4e, 0xde, 0x98, - 0xd7, 0xde, 0xa8, 0xdd, 0x98, 0x9c, 0x9b, 0xd4, 0x4a, 0x33, 0xa3, 0x3d, 0xe4, 0x51, 0x28, 0x76, - 0xa0, 0xa9, 0x4e, 0x4e, 0xdc, 0xd2, 0x2a, 0x8b, 0x6f, 0x8c, 0x66, 0xc8, 0x23, 0x70, 0xbe, 0x23, - 0x11, 0xfb, 0x3d, 0x9a, 0x25, 0x17, 0xe0, 0x6c, 0x27, 0x92, 0xd7, 0x67, 0x46, 0x73, 0x8f, 0xff, - 0x50, 0x06, 0x48, 0xf2, 0x1d, 0x38, 0x79, 0x18, 0xce, 0x31, 0xbd, 0xa8, 0x75, 0x6e, 0xe0, 0x23, - 0x70, 0x3e, 0x95, 0x42, 0x6a, 0x5e, 0x11, 0x1e, 0xea, 0x40, 0x22, 0x1a, 0x77, 0x0e, 0x94, 0x74, - 0x02, 0x6c, 0xda, 0x2f, 0x65, 0xe0, 0x64, 0xaa, 0x11, 0x49, 0x2e, 0xc1, 0x7b, 0x4a, 0xe5, 0x59, - 0xd6, 0x37, 0x13, 0x8b, 0x95, 0xf9, 0xb9, 0x6a, 0x6d, 0x76, 0xaa, 0x54, 0x63, 0xda, 0x77, 0xab, - 0x1a, 0xeb, 0xcd, 0x8b, 0xa0, 0x76, 0xa1, 0x9c, 0x98, 0x2e, 0xcd, 0xdd, 0x60, 0xc3, 0x8f, 0xbc, - 0x07, 0x1e, 0xee, 0x48, 0x37, 0x39, 0x57, 0x1a, 0x9f, 0x99, 0x2c, 0x8f, 0x66, 0xc9, 0x63, 0xf0, - 0x48, 0x47, 0xaa, 0x72, 0xa5, 0xca, 0xc9, 0x72, 0xe3, 0xe5, 0xb7, 0xff, 0xcd, 0x85, 0x9e, 0xb7, - 0xbf, 0x71, 0x21, 0xf3, 0x5b, 0xdf, 0xb8, 0x90, 0xf9, 0x83, 0x6f, 0x5c, 0xc8, 0x7c, 0xf8, 0xda, - 0x4e, 0xde, 0xf7, 0xf3, 0x69, 0x6b, 0xa9, 0x80, 0x13, 0xfa, 0xd3, 0xff, 0x3b, 0x00, 0x00, 0xff, - 0xff, 0x09, 0x71, 0x1b, 0xb1, 0x03, 0x57, 0x01, 0x00, + // 15652 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xbd, 0x59, 0x70, 0x24, 0xc9, + 0x75, 0x20, 0x88, 0x3c, 0x90, 0x00, 0x1e, 0x8e, 0x02, 0xbc, 0xae, 0xe8, 0xea, 0xaa, 0xca, 0xee, + 0x68, 0x76, 0xb1, 0xaa, 0xd9, 0x5d, 0xc5, 0xae, 0xae, 0xee, 0x66, 0x5f, 0xec, 0x4e, 0x20, 0x81, + 0x42, 0x76, 0xe1, 0xea, 0x48, 0x54, 0x15, 0x9b, 0x57, 0x2a, 0x90, 0xe1, 0x00, 0xa2, 0x91, 0x19, + 0x91, 0x8c, 0x88, 0x2c, 0x14, 0x7a, 0x2f, 0x51, 0xab, 0x83, 0xd4, 0x92, 0x14, 0x97, 0x5a, 0x89, + 0xd2, 0x4a, 0xbb, 0xa2, 0xae, 0x5d, 0xad, 0x4c, 0x2b, 0xad, 0xd6, 0x64, 0x92, 0x28, 0x2d, 0x6d, + 0xa5, 0xe5, 0x1e, 0xad, 0xa5, 0x69, 0x4d, 0xd2, 0xce, 0xc8, 0x64, 0x33, 0x1a, 0x50, 0xc3, 0x19, + 0xcd, 0x07, 0x6c, 0xc6, 0x4c, 0x33, 0x43, 0x1b, 0x71, 0x34, 0x9a, 0xb1, 0x31, 0x7f, 0xee, 0x11, + 0xe1, 0x71, 0x64, 0xe2, 0x6c, 0xa1, 0xc1, 0xc2, 0x4f, 0x15, 0xf2, 0xbd, 0xe7, 0xcf, 0x3d, 0x9e, + 0x3f, 0x77, 0x7f, 0xee, 0xfe, 0xfc, 0x3d, 0xb8, 0xe2, 0xd1, 0x06, 0x6d, 0xd9, 0x8e, 0x77, 0xad, + 0x41, 0x57, 0xf4, 0xfa, 0xc6, 0x35, 0x6f, 0xa3, 0x45, 0xdd, 0x6b, 0xf4, 0x1e, 0xb5, 0x3c, 0xff, + 0xbf, 0xab, 0x2d, 0xc7, 0xf6, 0x6c, 0x52, 0xe0, 0xbf, 0xce, 0x9d, 0x5a, 0xb1, 0x57, 0x6c, 0x04, + 0x5d, 0x63, 0x7f, 0x71, 0xec, 0xb9, 0xf3, 0x2b, 0xb6, 0xbd, 0xd2, 0xa0, 0xd7, 0xf0, 0xd7, 0x52, + 0x7b, 0xf9, 0x9a, 0xeb, 0x39, 0xed, 0xba, 0x27, 0xb0, 0xc5, 0x38, 0xd6, 0x33, 0x9b, 0xd4, 0xf5, + 0xf4, 0x66, 0x4b, 0x10, 0x5c, 0x8c, 0x13, 0xac, 0x3b, 0x7a, 0xab, 0x45, 0x1d, 0x51, 0xf9, 0xb9, + 0x47, 0xd3, 0xdb, 0x89, 0xff, 0x0a, 0x92, 0xa7, 0xd2, 0x49, 0x7c, 0x46, 0x31, 0x8e, 0xea, 0x97, + 0xb2, 0xd0, 0x3f, 0x4b, 0x3d, 0xdd, 0xd0, 0x3d, 0x9d, 0x9c, 0x87, 0xde, 0x8a, 0x65, 0xd0, 0xfb, + 0x4a, 0xe6, 0x91, 0xcc, 0xe5, 0xdc, 0x78, 0x61, 0x6b, 0xb3, 0x98, 0xa5, 0xa6, 0xc6, 0x81, 0xe4, + 0x02, 0xe4, 0x17, 0x37, 0x5a, 0x54, 0xc9, 0x3e, 0x92, 0xb9, 0x3c, 0x30, 0x3e, 0xb0, 0xb5, 0x59, + 0xec, 0x45, 0x59, 0x68, 0x08, 0x26, 0x8f, 0x42, 0xb6, 0x52, 0x56, 0x72, 0x88, 0x1c, 0xdb, 0xda, + 0x2c, 0x0e, 0xb7, 0x4d, 0xe3, 0x49, 0xbb, 0x69, 0x7a, 0xb4, 0xd9, 0xf2, 0x36, 0xb4, 0x6c, 0xa5, + 0x4c, 0x2e, 0x41, 0x7e, 0xc2, 0x36, 0xa8, 0x92, 0x47, 0x22, 0xb2, 0xb5, 0x59, 0x1c, 0xa9, 0xdb, + 0x06, 0x95, 0xa8, 0x10, 0x4f, 0x5e, 0x83, 0xfc, 0xa2, 0xd9, 0xa4, 0x4a, 0xef, 0x23, 0x99, 0xcb, + 0x83, 0xd7, 0xcf, 0x5d, 0xe5, 0x52, 0xb9, 0xea, 0x4b, 0xe5, 0xea, 0xa2, 0x2f, 0xb6, 0xf1, 0xd1, + 0x77, 0x36, 0x8b, 0x3d, 0x5b, 0x9b, 0xc5, 0x3c, 0x93, 0xe4, 0x17, 0xbf, 0x59, 0xcc, 0x68, 0x58, + 0x92, 0xbc, 0x0c, 0x83, 0x13, 0x8d, 0xb6, 0xeb, 0x51, 0x67, 0x4e, 0x6f, 0x52, 0xa5, 0x80, 0x15, + 0x9e, 0xdb, 0xda, 0x2c, 0x9e, 0xa9, 0x73, 0x70, 0xcd, 0xd2, 0x9b, 0x72, 0xc5, 0x32, 0xb9, 0xfa, + 0xdb, 0x19, 0x38, 0x51, 0xa5, 0xae, 0x6b, 0xda, 0x56, 0x20, 0x9b, 0xc7, 0x61, 0x40, 0x80, 0x2a, + 0x65, 0x94, 0xcf, 0xc0, 0x78, 0xdf, 0xd6, 0x66, 0x31, 0xe7, 0x9a, 0x86, 0x16, 0x62, 0xc8, 0x07, + 0xa1, 0xef, 0xae, 0xe9, 0xad, 0xce, 0x4e, 0x95, 0x84, 0x9c, 0xce, 0x6c, 0x6d, 0x16, 0xc9, 0xba, + 0xe9, 0xad, 0xd6, 0x9a, 0xcb, 0xba, 0x54, 0xa1, 0x4f, 0x46, 0x66, 0x60, 0x74, 0xc1, 0x31, 0xef, + 0xe9, 0x1e, 0xbd, 0x45, 0x37, 0x16, 0xec, 0x86, 0x59, 0xdf, 0x10, 0x52, 0x7c, 0x64, 0x6b, 0xb3, + 0x78, 0xbe, 0xc5, 0x71, 0xb5, 0x35, 0xba, 0x51, 0x6b, 0x21, 0x56, 0x62, 0x92, 0x28, 0xa9, 0xfe, + 0x4e, 0x01, 0x86, 0x6e, 0xbb, 0xd4, 0x09, 0xda, 0x7d, 0x09, 0xf2, 0xec, 0xb7, 0x68, 0x32, 0xca, + 0xbc, 0xed, 0x52, 0x47, 0x96, 0x39, 0xc3, 0x93, 0x2b, 0xd0, 0x3b, 0x63, 0xaf, 0x98, 0x96, 0x68, + 0xf6, 0xc9, 0xad, 0xcd, 0xe2, 0x89, 0x06, 0x03, 0x48, 0x94, 0x9c, 0x82, 0x7c, 0x18, 0x86, 0x2a, + 0x4d, 0xa6, 0x43, 0xb6, 0xa5, 0x7b, 0xb6, 0x23, 0x5a, 0x8b, 0xd2, 0x35, 0x25, 0xb8, 0x54, 0x30, + 0x42, 0x4f, 0x5e, 0x04, 0x28, 0xdd, 0xad, 0x6a, 0x76, 0x83, 0x96, 0xb4, 0x39, 0xa1, 0x0c, 0x58, + 0x5a, 0x5f, 0x77, 0x6b, 0x8e, 0xdd, 0xa0, 0x35, 0xdd, 0x91, 0xab, 0x95, 0xa8, 0xc9, 0x24, 0x8c, + 0x94, 0xea, 0x75, 0xea, 0xba, 0x1a, 0xfd, 0x54, 0x9b, 0xba, 0x9e, 0xab, 0xf4, 0x3e, 0x92, 0xbb, + 0x3c, 0x30, 0x7e, 0x61, 0x6b, 0xb3, 0xf8, 0x90, 0x8e, 0x98, 0x9a, 0x23, 0x50, 0x12, 0x8b, 0x58, + 0x21, 0x32, 0x0e, 0xc3, 0xa5, 0xb7, 0xdb, 0x0e, 0xad, 0x18, 0xd4, 0xf2, 0x4c, 0x6f, 0x43, 0x68, + 0xc8, 0xf9, 0xad, 0xcd, 0xa2, 0xa2, 0x33, 0x44, 0xcd, 0x14, 0x18, 0x89, 0x49, 0xb4, 0x08, 0x99, + 0x87, 0xb1, 0x9b, 0x13, 0x0b, 0x55, 0xea, 0xdc, 0x33, 0xeb, 0xb4, 0x54, 0xaf, 0xdb, 0x6d, 0xcb, + 0x53, 0xfa, 0x90, 0xcf, 0xa3, 0x5b, 0x9b, 0xc5, 0x0b, 0x2b, 0xf5, 0x56, 0xcd, 0xe5, 0xd8, 0x9a, + 0xce, 0xd1, 0x12, 0xb3, 0x64, 0x59, 0xf2, 0x51, 0x18, 0x5e, 0x74, 0x98, 0x16, 0x1a, 0x65, 0xca, + 0xe0, 0x4a, 0x3f, 0xea, 0xff, 0x99, 0xab, 0x62, 0x02, 0xe2, 0x50, 0xbf, 0x67, 0x79, 0x63, 0x3d, + 0x5e, 0xa0, 0x66, 0x20, 0x4e, 0x6e, 0x6c, 0x84, 0x15, 0xa1, 0xa0, 0xb0, 0x8f, 0x37, 0x1d, 0x6a, + 0x24, 0xb4, 0x6d, 0x00, 0xdb, 0x7c, 0x65, 0x6b, 0xb3, 0xf8, 0xb8, 0x23, 0x68, 0x6a, 0x5d, 0xd5, + 0xae, 0x23, 0x2b, 0x32, 0x09, 0xfd, 0x4c, 0x9b, 0x6e, 0x99, 0x96, 0xa1, 0xc0, 0x23, 0x99, 0xcb, + 0x23, 0xd7, 0x47, 0xfd, 0xd6, 0xfb, 0xf0, 0xf1, 0xb3, 0x5b, 0x9b, 0xc5, 0x93, 0x4c, 0x07, 0x6b, + 0x6b, 0xa6, 0x25, 0x4f, 0x11, 0x41, 0x51, 0x36, 0x8a, 0xc6, 0x6d, 0x0f, 0x87, 0xee, 0x60, 0x38, + 0x8a, 0x96, 0x6c, 0x2f, 0x3e, 0x6c, 0x7d, 0x32, 0x32, 0x01, 0xc3, 0xe3, 0xb6, 0x57, 0xb1, 0x5c, + 0x4f, 0xb7, 0xea, 0xb4, 0x52, 0x56, 0x86, 0xb0, 0x1c, 0xaa, 0x05, 0x2b, 0x67, 0x0a, 0x4c, 0x2d, + 0x32, 0x29, 0x45, 0xcb, 0xa8, 0x7f, 0x9d, 0x87, 0x11, 0xd6, 0x27, 0xd2, 0xf0, 0x29, 0xb1, 0x99, + 0x80, 0x41, 0x58, 0x2d, 0x6e, 0x4b, 0xaf, 0x53, 0x31, 0x92, 0xf0, 0x2b, 0x2c, 0x1f, 0x28, 0xf1, + 0x8c, 0xd3, 0x93, 0x2b, 0xd0, 0xcf, 0x41, 0x95, 0xb2, 0x18, 0x5c, 0xc3, 0x5b, 0x9b, 0xc5, 0x01, + 0x17, 0x61, 0x35, 0xd3, 0xd0, 0x02, 0x34, 0xd3, 0x6e, 0xfe, 0xf7, 0xb4, 0xed, 0x7a, 0x8c, 0xb9, + 0x18, 0x5b, 0xf8, 0x19, 0xa2, 0xc0, 0xaa, 0x40, 0xc9, 0xda, 0x1d, 0x2d, 0x44, 0x5e, 0x00, 0xe0, + 0x90, 0x92, 0x61, 0x38, 0x62, 0x80, 0x3d, 0xb4, 0xb5, 0x59, 0x3c, 0x2d, 0x58, 0xe8, 0x86, 0x21, + 0x8f, 0x4e, 0x89, 0x98, 0x34, 0x61, 0x88, 0xff, 0x9a, 0xd1, 0x97, 0x68, 0x83, 0x8f, 0xae, 0xc1, + 0xeb, 0x97, 0xfd, 0x4e, 0x8c, 0x4a, 0xe7, 0xaa, 0x4c, 0x3a, 0x69, 0x79, 0xce, 0xc6, 0x78, 0x51, + 0x4c, 0xc8, 0x67, 0x45, 0x55, 0x0d, 0xc4, 0xc9, 0x53, 0x81, 0x5c, 0x86, 0xcd, 0xd3, 0x53, 0xb6, + 0xb3, 0xae, 0x3b, 0x06, 0x35, 0xc6, 0x37, 0xe4, 0x79, 0x7a, 0xd9, 0x07, 0xd7, 0x96, 0x64, 0xd5, + 0x93, 0xc9, 0x59, 0xa7, 0x73, 0x6e, 0xd5, 0xf6, 0x12, 0xaa, 0x5c, 0x5f, 0x42, 0x5a, 0x6e, 0x7b, + 0x29, 0xae, 0x66, 0xd1, 0x32, 0x6c, 0x2a, 0xe0, 0x80, 0x3b, 0xd4, 0x61, 0x93, 0x38, 0x8e, 0x3a, + 0x31, 0x15, 0x08, 0x26, 0xf7, 0x38, 0x26, 0xc9, 0x43, 0x14, 0x39, 0xf7, 0x2a, 0x8c, 0x25, 0x44, + 0x41, 0x46, 0x21, 0xb7, 0x46, 0x37, 0xb8, 0xba, 0x68, 0xec, 0x4f, 0x72, 0x0a, 0x7a, 0xef, 0xe9, + 0x8d, 0xb6, 0x58, 0x42, 0x35, 0xfe, 0xe3, 0xc5, 0xec, 0x87, 0x32, 0x6c, 0xc5, 0x21, 0x13, 0xb6, + 0x65, 0xd1, 0xba, 0x27, 0x2f, 0x3a, 0xcf, 0xc1, 0xc0, 0x8c, 0x5d, 0xd7, 0x1b, 0xd8, 0x8f, 0x5c, + 0xef, 0x94, 0xad, 0xcd, 0xe2, 0x29, 0xd6, 0x81, 0x57, 0x1b, 0x0c, 0x23, 0xb5, 0x29, 0x24, 0x65, + 0x0a, 0xa0, 0xd1, 0xa6, 0xed, 0x51, 0x2c, 0x98, 0x0d, 0x15, 0x00, 0x0b, 0x3a, 0x88, 0x92, 0x15, + 0x20, 0x24, 0x26, 0xd7, 0xa0, 0x7f, 0x81, 0xad, 0xb3, 0x75, 0xbb, 0x21, 0x94, 0x0f, 0x97, 0x02, + 0x5c, 0x7b, 0xe5, 0xb1, 0xea, 0x13, 0xa9, 0xd3, 0x30, 0x32, 0xd1, 0x30, 0xa9, 0xe5, 0xc9, 0xad, + 0x66, 0x23, 0xb9, 0xb4, 0x42, 0x2d, 0x4f, 0x6e, 0x35, 0x8e, 0x79, 0x9d, 0x41, 0xe5, 0x56, 0x07, + 0xa4, 0xea, 0xff, 0x97, 0x83, 0x87, 0x6e, 0xb5, 0x97, 0xa8, 0x63, 0x51, 0x8f, 0xba, 0x62, 0x41, + 0x0e, 0xb8, 0xce, 0xc1, 0x58, 0x02, 0x29, 0xb8, 0xe3, 0x42, 0xb9, 0x16, 0x20, 0x6b, 0x62, 0x8d, + 0x97, 0x67, 0xdb, 0x44, 0x51, 0x32, 0x0d, 0x27, 0x42, 0x20, 0x6b, 0x84, 0xab, 0x64, 0x71, 0x29, + 0xb9, 0xb8, 0xb5, 0x59, 0x3c, 0x27, 0x71, 0x63, 0xcd, 0x96, 0x35, 0x38, 0x5e, 0x8c, 0xdc, 0x82, + 0xd1, 0x10, 0x74, 0xd3, 0xb1, 0xdb, 0x2d, 0x57, 0xc9, 0x21, 0xab, 0xe2, 0xd6, 0x66, 0xf1, 0x61, + 0x89, 0xd5, 0x0a, 0x22, 0xe5, 0x05, 0x3c, 0x5e, 0x90, 0x7c, 0x7f, 0x46, 0xe6, 0x26, 0x46, 0x61, + 0x1e, 0x47, 0xe1, 0xf3, 0xfe, 0x28, 0xec, 0x28, 0xa4, 0xab, 0xf1, 0x92, 0x62, 0x50, 0xc6, 0x9a, + 0x91, 0x18, 0x94, 0x89, 0x1a, 0xcf, 0x4d, 0xc0, 0xe9, 0x54, 0x5e, 0xbb, 0xd2, 0xea, 0xbf, 0xcc, + 0xc9, 0x5c, 0x16, 0x6c, 0x23, 0xe8, 0xcc, 0x79, 0xb9, 0x33, 0x17, 0x6c, 0x03, 0xa7, 0xfa, 0x4c, + 0xb8, 0x76, 0x4a, 0x8d, 0x6d, 0xd9, 0x46, 0x7c, 0xd6, 0x4f, 0x96, 0x25, 0x9f, 0x84, 0x33, 0x09, + 0x20, 0x9f, 0xae, 0xb9, 0xf6, 0x5f, 0xda, 0xda, 0x2c, 0xaa, 0x29, 0x5c, 0xe3, 0xb3, 0x77, 0x07, + 0x2e, 0x44, 0x87, 0xb3, 0x92, 0xd4, 0x6d, 0xcb, 0xd3, 0x4d, 0x4b, 0x18, 0x97, 0x7c, 0x94, 0xbc, + 0x7f, 0x6b, 0xb3, 0xf8, 0x98, 0xac, 0x83, 0x3e, 0x4d, 0xbc, 0xf1, 0x9d, 0xf8, 0x10, 0x03, 0x94, + 0x14, 0x54, 0xa5, 0xa9, 0xaf, 0xf8, 0x16, 0xf3, 0xe5, 0xad, 0xcd, 0xe2, 0xfb, 0x52, 0xeb, 0x30, + 0x19, 0x95, 0xbc, 0x42, 0x77, 0xe2, 0x44, 0x34, 0x20, 0x21, 0x6e, 0xce, 0x36, 0x28, 0x7e, 0x43, + 0x2f, 0xf2, 0x57, 0xb7, 0x36, 0x8b, 0x17, 0x25, 0xfe, 0x96, 0x6d, 0xd0, 0x78, 0xf3, 0x53, 0x4a, + 0xab, 0xbf, 0x9d, 0x83, 0x8b, 0xd5, 0xd2, 0xec, 0x4c, 0xc5, 0xf0, 0x4d, 0x9a, 0x05, 0xc7, 0xbe, + 0x67, 0x1a, 0xd2, 0xe8, 0x5d, 0x82, 0xb3, 0x31, 0xd4, 0x24, 0x5a, 0x51, 0x81, 0x31, 0x8d, 0xdf, + 0xe6, 0x9b, 0x4b, 0x2d, 0x41, 0x53, 0xe3, 0xa6, 0x56, 0x74, 0xd1, 0xee, 0xc4, 0x88, 0xf5, 0x51, + 0x0c, 0x55, 0x5d, 0xb5, 0x1d, 0xaf, 0xde, 0xf6, 0x84, 0x12, 0x60, 0x1f, 0x25, 0xea, 0x70, 0x05, + 0x51, 0x97, 0x2a, 0x7c, 0x3e, 0xe4, 0xb3, 0x19, 0x18, 0x2d, 0x79, 0x9e, 0x63, 0x2e, 0xb5, 0x3d, + 0x3a, 0xab, 0xb7, 0x5a, 0xa6, 0xb5, 0x82, 0x63, 0x7d, 0xf0, 0xfa, 0xcb, 0xc1, 0x1a, 0xd9, 0x55, + 0x12, 0x57, 0xe3, 0xc5, 0xa5, 0x21, 0xaa, 0xfb, 0xa8, 0x5a, 0x93, 0xe3, 0xe4, 0x21, 0x1a, 0x2f, + 0xc7, 0x86, 0x68, 0x2a, 0xaf, 0x5d, 0x0d, 0xd1, 0x2f, 0xe5, 0xe0, 0xfc, 0xfc, 0x9a, 0xa7, 0x6b, + 0xd4, 0xb5, 0xdb, 0x4e, 0x9d, 0xba, 0xb7, 0x5b, 0x86, 0xee, 0xd1, 0x70, 0xa4, 0x16, 0xa1, 0xb7, + 0x64, 0x18, 0xd4, 0x40, 0x76, 0xbd, 0x7c, 0xdb, 0xa7, 0x33, 0x80, 0xc6, 0xe1, 0xe4, 0x71, 0xe8, + 0x13, 0x65, 0x90, 0x7b, 0xef, 0xf8, 0xe0, 0xd6, 0x66, 0xb1, 0xaf, 0xcd, 0x41, 0x9a, 0x8f, 0x63, + 0x64, 0x65, 0xda, 0xa0, 0x8c, 0x2c, 0x17, 0x92, 0x19, 0x1c, 0xa4, 0xf9, 0x38, 0xf2, 0x06, 0x8c, + 0x20, 0xdb, 0xa0, 0x3d, 0x62, 0xee, 0x3b, 0xe5, 0x4b, 0x57, 0x6e, 0x2c, 0x5f, 0x9a, 0xb0, 0x35, + 0x35, 0xc7, 0x2f, 0xa0, 0xc5, 0x18, 0x90, 0xbb, 0x30, 0x2a, 0x1a, 0x11, 0x32, 0xed, 0xed, 0xc2, + 0xf4, 0xf4, 0xd6, 0x66, 0x71, 0x4c, 0xb4, 0x5f, 0x62, 0x9b, 0x60, 0xc2, 0x18, 0x8b, 0x66, 0x87, + 0x8c, 0x0b, 0xdb, 0x31, 0x16, 0x5f, 0x2c, 0x33, 0x8e, 0x33, 0x51, 0xdf, 0x84, 0x21, 0xb9, 0x20, + 0x39, 0x83, 0x5b, 0x6b, 0x3e, 0x4e, 0x70, 0x53, 0x6e, 0x1a, 0xb8, 0x9f, 0x7e, 0x1a, 0x06, 0xcb, + 0xd4, 0xad, 0x3b, 0x66, 0x8b, 0x59, 0x0d, 0x42, 0xc9, 0x4f, 0x6c, 0x6d, 0x16, 0x07, 0x8d, 0x10, + 0xac, 0xc9, 0x34, 0xea, 0xbf, 0xc9, 0xc0, 0x19, 0xc6, 0xbb, 0xe4, 0xba, 0xe6, 0x8a, 0xd5, 0x94, + 0x97, 0xed, 0x27, 0xa1, 0x50, 0xc5, 0xfa, 0x44, 0x4d, 0xa7, 0xb6, 0x36, 0x8b, 0xa3, 0xbc, 0x05, + 0x92, 0x1e, 0x0a, 0x9a, 0x60, 0x5f, 0x99, 0xdd, 0x66, 0x5f, 0xc9, 0x4c, 0x5a, 0x4f, 0x77, 0x3c, + 0xd3, 0x5a, 0xa9, 0x7a, 0xba, 0xd7, 0x76, 0x23, 0x26, 0xad, 0xc0, 0xd4, 0x5c, 0x44, 0x45, 0x4c, + 0xda, 0x48, 0x21, 0xf2, 0x2a, 0x0c, 0x4d, 0x5a, 0x46, 0xc8, 0x84, 0x4f, 0x88, 0x0f, 0x33, 0x4b, + 0x93, 0x22, 0x3c, 0xc9, 0x22, 0x52, 0x40, 0xfd, 0x9f, 0x33, 0xa0, 0xf0, 0x4d, 0xe0, 0x8c, 0xe9, + 0x7a, 0xb3, 0xb4, 0xb9, 0x24, 0xcd, 0x4e, 0x53, 0xfe, 0xae, 0x92, 0xe1, 0xa4, 0xb5, 0x08, 0x4d, + 0x01, 0xb1, 0xab, 0x6c, 0x98, 0x6e, 0x62, 0xfb, 0x11, 0x2b, 0x45, 0x2a, 0xd0, 0xc7, 0x39, 0x73, + 0x5b, 0x62, 0xf0, 0xba, 0xe2, 0x2b, 0x42, 0xbc, 0x6a, 0xae, 0x0c, 0x4d, 0x4e, 0x2c, 0x6f, 0x68, + 0x44, 0x79, 0xf5, 0x7f, 0xc9, 0xc2, 0x68, 0xbc, 0x10, 0xb9, 0x0b, 0xfd, 0xaf, 0xdb, 0xa6, 0x45, + 0x8d, 0x79, 0x0b, 0x5b, 0xd8, 0xfd, 0x70, 0xc4, 0xb7, 0xc5, 0x4f, 0xbe, 0x85, 0x65, 0x6a, 0xb2, + 0x05, 0x8b, 0x67, 0x25, 0x01, 0x33, 0xf2, 0x51, 0x18, 0x60, 0x36, 0xe0, 0x3d, 0xe4, 0x9c, 0xdd, + 0x96, 0xf3, 0x23, 0x82, 0xf3, 0x29, 0x87, 0x17, 0x4a, 0xb2, 0x0e, 0xd9, 0x31, 0xbd, 0xd2, 0xa8, + 0xee, 0xda, 0x96, 0xe8, 0x79, 0xd4, 0x2b, 0x07, 0x21, 0xb2, 0x5e, 0x71, 0x1a, 0x66, 0xba, 0xf2, + 0x8f, 0xc5, 0x6e, 0x90, 0xf6, 0x2e, 0x5c, 0x56, 0xf1, 0x1e, 0x90, 0x88, 0xd5, 0x1f, 0xcc, 0xc2, + 0x53, 0xa1, 0xc8, 0x34, 0x7a, 0xcf, 0xa4, 0xeb, 0x42, 0x9c, 0xab, 0x66, 0x4b, 0xec, 0x59, 0x99, + 0xca, 0xbb, 0x13, 0xab, 0xba, 0xb5, 0x42, 0x0d, 0x72, 0x05, 0x7a, 0x35, 0xbb, 0x41, 0x5d, 0x25, + 0x83, 0xe6, 0x1a, 0x4e, 0x27, 0x0e, 0x03, 0xc8, 0x87, 0x1e, 0x48, 0x41, 0x6c, 0x28, 0x2c, 0x3a, + 0xba, 0xe9, 0xf9, 0x3d, 0x5b, 0x4a, 0xf6, 0xec, 0x0e, 0x6a, 0xbc, 0xca, 0x79, 0xf0, 0x39, 0x1f, + 0x05, 0xe1, 0x21, 0x40, 0x16, 0x04, 0x27, 0x39, 0xf7, 0x02, 0x0c, 0x4a, 0xc4, 0xbb, 0x9a, 0xd4, + 0xbf, 0x9a, 0x97, 0x75, 0xdd, 0x6f, 0x96, 0xd0, 0xf5, 0x6b, 0x4c, 0x47, 0x5d, 0x97, 0x59, 0x15, + 0x5c, 0xc9, 0x85, 0x26, 0x22, 0x28, 0xaa, 0x89, 0x08, 0x22, 0xcf, 0x40, 0x3f, 0x67, 0x11, 0xec, + 0x5f, 0x71, 0xef, 0xeb, 0x20, 0x2c, 0xba, 0x34, 0x07, 0x84, 0xe4, 0x97, 0x33, 0x70, 0xa1, 0xab, + 0x24, 0x50, 0x19, 0x06, 0xaf, 0x3f, 0xbb, 0x27, 0x31, 0x8e, 0x3f, 0xb5, 0xb5, 0x59, 0xbc, 0xd2, + 0x0c, 0x48, 0x6a, 0x8e, 0x44, 0x53, 0xab, 0x73, 0x22, 0xa9, 0x5d, 0xdd, 0x9b, 0xc2, 0x8c, 0x47, + 0x5e, 0xe9, 0x14, 0x1e, 0x1d, 0x59, 0xf5, 0x0d, 0xbf, 0x91, 0xf9, 0xd0, 0x78, 0x14, 0xdf, 0xbb, + 0xec, 0x93, 0xa4, 0x54, 0xd3, 0x81, 0x0b, 0xa9, 0xc3, 0x59, 0x8e, 0x29, 0xeb, 0x1b, 0xf3, 0xcb, + 0xb3, 0xb6, 0xe5, 0xad, 0xfa, 0x15, 0xf4, 0xca, 0x67, 0x2f, 0x58, 0x81, 0xa1, 0x6f, 0xd4, 0xec, + 0xe5, 0x5a, 0x93, 0x51, 0xa5, 0xd4, 0xd1, 0x89, 0x13, 0x9b, 0x68, 0xc5, 0x98, 0xf3, 0xa7, 0xa0, + 0x42, 0x78, 0x32, 0xe6, 0x8f, 0xd3, 0xe4, 0x84, 0x13, 0x2b, 0xa4, 0x56, 0x60, 0x68, 0xc6, 0xae, + 0xaf, 0x05, 0xea, 0xf2, 0x02, 0x14, 0x16, 0x75, 0x67, 0x85, 0x7a, 0x28, 0x8b, 0xc1, 0xeb, 0x63, + 0x57, 0xf9, 0x69, 0x33, 0x23, 0xe2, 0x88, 0xf1, 0x11, 0x31, 0x1b, 0x14, 0x3c, 0xfc, 0xad, 0x89, + 0x02, 0xea, 0x37, 0x7b, 0x61, 0x48, 0x9c, 0x8c, 0xe2, 0x6c, 0x4e, 0x5e, 0x0c, 0xcf, 0x9a, 0xc5, + 0xf4, 0x15, 0x9c, 0x0e, 0x05, 0xa7, 0x5a, 0x43, 0x8c, 0xd9, 0x1f, 0x6d, 0x16, 0x33, 0x5b, 0x9b, + 0xc5, 0x1e, 0xad, 0x5f, 0xda, 0x54, 0x86, 0xeb, 0x8d, 0xb4, 0xc0, 0xca, 0x67, 0x9d, 0xb1, 0xb2, + 0x7c, 0xfd, 0x79, 0x15, 0xfa, 0x44, 0x1b, 0x84, 0xc6, 0x9d, 0x0d, 0xcf, 0x32, 0x22, 0x27, 0xbc, + 0xb1, 0xd2, 0x7e, 0x29, 0xf2, 0x32, 0x14, 0xf8, 0xde, 0x5e, 0x08, 0xe0, 0x4c, 0xfa, 0x59, 0x48, + 0xac, 0xb8, 0x28, 0x43, 0xa6, 0x01, 0xc2, 0x7d, 0x7d, 0x70, 0xa0, 0x2d, 0x38, 0x24, 0x77, 0xfc, + 0x31, 0x2e, 0x52, 0x59, 0xf2, 0x1c, 0x0c, 0x2d, 0x52, 0xa7, 0x69, 0x5a, 0x7a, 0xa3, 0x6a, 0xbe, + 0xed, 0x9f, 0x69, 0xe3, 0xc2, 0xeb, 0x9a, 0x6f, 0xcb, 0x23, 0x37, 0x42, 0x47, 0x3e, 0x91, 0xb6, + 0x6f, 0xee, 0xc3, 0x86, 0x3c, 0xba, 0xed, 0x86, 0x32, 0xd6, 0x9e, 0x94, 0x6d, 0xf4, 0x1b, 0x30, + 0x1c, 0xd9, 0x32, 0x89, 0x43, 0xcb, 0x0b, 0x49, 0xd6, 0xd2, 0xfe, 0x2f, 0xc6, 0x36, 0xca, 0x81, + 0x69, 0x72, 0xc5, 0x32, 0x3d, 0x53, 0x6f, 0x4c, 0xd8, 0xcd, 0xa6, 0x6e, 0x19, 0xca, 0x40, 0xa8, + 0xc9, 0x26, 0xc7, 0xd4, 0xea, 0x1c, 0x25, 0x6b, 0x72, 0xb4, 0x10, 0xdb, 0x96, 0x8b, 0x3e, 0xd4, + 0x68, 0xdd, 0x76, 0x98, 0x2d, 0x80, 0x67, 0x92, 0x62, 0x5b, 0xee, 0x72, 0x5c, 0xcd, 0xf1, 0x91, + 0xb2, 0xb1, 0x1d, 0x2f, 0xf8, 0x7a, 0xbe, 0x7f, 0x70, 0x74, 0x28, 0x7e, 0x8c, 0xac, 0xfe, 0x4f, + 0x39, 0x18, 0x14, 0xa4, 0x6c, 0x29, 0x3d, 0x56, 0xf0, 0xfd, 0x28, 0x78, 0xaa, 0xa2, 0x16, 0x0e, + 0x4a, 0x51, 0xd5, 0xcf, 0x65, 0x83, 0xd9, 0x68, 0xc1, 0x31, 0xad, 0xfd, 0xcd, 0x46, 0x97, 0x00, + 0x26, 0x56, 0xdb, 0xd6, 0x1a, 0xbf, 0x2e, 0xcb, 0x86, 0xd7, 0x65, 0x75, 0x53, 0x93, 0x30, 0xe4, + 0x02, 0xe4, 0xcb, 0x8c, 0x3f, 0xeb, 0x99, 0xa1, 0xf1, 0x81, 0x77, 0x38, 0xa7, 0xcc, 0x53, 0x1a, + 0x82, 0xd9, 0xe6, 0x6a, 0x7c, 0xc3, 0xa3, 0xdc, 0x9c, 0xcd, 0xf1, 0xcd, 0xd5, 0x12, 0x03, 0x68, + 0x1c, 0x4e, 0x6e, 0xc0, 0x58, 0x99, 0x36, 0xf4, 0x8d, 0x59, 0xb3, 0xd1, 0x30, 0x5d, 0x5a, 0xb7, + 0x2d, 0xc3, 0x45, 0x21, 0x8b, 0xea, 0x9a, 0xae, 0x96, 0x24, 0x20, 0x2a, 0x14, 0xe6, 0x97, 0x97, + 0x5d, 0xea, 0xa1, 0xf8, 0x72, 0xe3, 0xc0, 0x26, 0x67, 0x1b, 0x21, 0x9a, 0xc0, 0xa8, 0xbf, 0x96, + 0x61, 0xbb, 0x17, 0x77, 0xcd, 0xb3, 0x5b, 0x81, 0x96, 0xef, 0x4b, 0x24, 0x57, 0x42, 0xbb, 0x22, + 0x8b, 0x5f, 0x7b, 0x42, 0x7c, 0x6d, 0x9f, 0xb0, 0x2d, 0x42, 0x8b, 0x22, 0xf5, 0xab, 0x72, 0xdb, + 0x7c, 0x95, 0xfa, 0x57, 0x59, 0x38, 0x2b, 0x5a, 0x3c, 0xd1, 0x30, 0x5b, 0x4b, 0xb6, 0xee, 0x18, + 0x1a, 0xad, 0x53, 0xf3, 0x1e, 0x3d, 0x9a, 0x03, 0x2f, 0x3a, 0x74, 0xf2, 0xfb, 0x18, 0x3a, 0xd7, + 0x71, 0x23, 0xc8, 0x24, 0x83, 0x07, 0xbe, 0xdc, 0xa8, 0x18, 0xdd, 0xda, 0x2c, 0x0e, 0x19, 0x1c, + 0x8c, 0x47, 0xfe, 0x9a, 0x4c, 0xc4, 0x94, 0x64, 0x86, 0x5a, 0x2b, 0xde, 0x2a, 0x2a, 0x49, 0x2f, + 0x57, 0x92, 0x06, 0x42, 0x34, 0x81, 0x51, 0xff, 0x45, 0x16, 0x4e, 0xc5, 0x45, 0x5e, 0xa5, 0x96, + 0x71, 0x2c, 0xef, 0x77, 0x47, 0xde, 0xdf, 0xce, 0xc1, 0xc3, 0xa2, 0x4c, 0x75, 0x55, 0x77, 0xa8, + 0x51, 0x36, 0x1d, 0x5a, 0xf7, 0x6c, 0x67, 0xe3, 0x08, 0x1b, 0x50, 0x07, 0x27, 0xf6, 0x1b, 0x50, + 0x10, 0xdb, 0x7f, 0xbe, 0xce, 0x8c, 0x04, 0x2d, 0x41, 0x68, 0x62, 0x85, 0xe2, 0x47, 0x07, 0xb1, + 0xce, 0x2a, 0xec, 0xa4, 0xb3, 0x3e, 0x04, 0xc3, 0x81, 0xe8, 0x71, 0x23, 0xda, 0x17, 0x5a, 0x5b, + 0x86, 0x8f, 0xc0, 0xbd, 0xa8, 0x16, 0x25, 0xc4, 0xda, 0x7c, 0x40, 0xa5, 0x8c, 0xd6, 0xd0, 0xb0, + 0xa8, 0x2d, 0x28, 0x67, 0x1a, 0x9a, 0x4c, 0xa4, 0x6e, 0xe6, 0xe1, 0x5c, 0x7a, 0xb7, 0x6b, 0x54, + 0x37, 0x8e, 0x7b, 0xfd, 0xbb, 0xb2, 0xd7, 0xc9, 0xa3, 0x90, 0x5f, 0xd0, 0xbd, 0x55, 0x71, 0xfd, + 0x8e, 0x77, 0xc2, 0xcb, 0x66, 0x83, 0xd6, 0x5a, 0xba, 0xb7, 0xaa, 0x21, 0x4a, 0x9a, 0x33, 0x00, + 0x39, 0xa6, 0xcc, 0x19, 0xd2, 0x62, 0x3f, 0xf8, 0x48, 0xe6, 0x72, 0x3e, 0x75, 0xb1, 0xff, 0x66, + 0xbe, 0xd3, 0xbc, 0x72, 0xd7, 0x31, 0x3d, 0x7a, 0xac, 0x61, 0xc7, 0x1a, 0xb6, 0x4f, 0x0d, 0xfb, + 0x93, 0x2c, 0x0c, 0x07, 0x9b, 0xa6, 0xb7, 0x68, 0xfd, 0x70, 0xd6, 0xaa, 0x70, 0x2b, 0x93, 0xdb, + 0xf7, 0x56, 0x66, 0x3f, 0x0a, 0xa5, 0x06, 0x47, 0x9e, 0xdc, 0x34, 0x40, 0x89, 0xf1, 0x23, 0xcf, + 0xe0, 0xa0, 0xf3, 0x51, 0xe8, 0x9b, 0xd5, 0xef, 0x9b, 0xcd, 0x76, 0x53, 0x58, 0xe9, 0xe8, 0x4e, + 0xd6, 0xd4, 0xef, 0x6b, 0x3e, 0x5c, 0xfd, 0x7b, 0x19, 0x18, 0x11, 0x42, 0x15, 0xcc, 0xf7, 0x25, + 0xd5, 0x50, 0x3a, 0xd9, 0x7d, 0x4b, 0x27, 0xb7, 0x77, 0xe9, 0xa8, 0x3f, 0x9d, 0x03, 0x65, 0xca, + 0x6c, 0xd0, 0x45, 0x47, 0xb7, 0xdc, 0x65, 0xea, 0x88, 0xed, 0xf4, 0x24, 0x63, 0xb5, 0xaf, 0x0f, + 0x94, 0xa6, 0x94, 0xec, 0x9e, 0xa6, 0x94, 0x0f, 0xc0, 0x80, 0x68, 0x4c, 0xe0, 0xca, 0x88, 0xa3, + 0xc6, 0xf1, 0x81, 0x5a, 0x88, 0x67, 0xc4, 0xa5, 0x56, 0xcb, 0xb1, 0xef, 0x51, 0x87, 0xdf, 0x52, + 0x09, 0x62, 0xdd, 0x07, 0x6a, 0x21, 0x5e, 0xe2, 0x4c, 0x7d, 0x7b, 0x51, 0xe6, 0x4c, 0x1d, 0x2d, + 0xc4, 0x93, 0xcb, 0xd0, 0x3f, 0x63, 0xd7, 0x75, 0x14, 0x34, 0x9f, 0x56, 0x86, 0xb6, 0x36, 0x8b, + 0xfd, 0x0d, 0x01, 0xd3, 0x02, 0x2c, 0xa3, 0x2c, 0xdb, 0xeb, 0x56, 0xc3, 0xd6, 0xb9, 0xf3, 0x4b, + 0x3f, 0xa7, 0x34, 0x04, 0x4c, 0x0b, 0xb0, 0x8c, 0x92, 0xc9, 0x1c, 0x9d, 0x8a, 0xfa, 0x43, 0x9e, + 0xcb, 0x02, 0xa6, 0x05, 0x58, 0xf5, 0xd7, 0xf2, 0x4c, 0x7b, 0x5d, 0xf3, 0xed, 0x07, 0x7e, 0x5d, + 0x08, 0x07, 0x4c, 0xef, 0x1e, 0x06, 0xcc, 0x03, 0x73, 0x60, 0xa7, 0xfe, 0x75, 0x1f, 0x80, 0x90, + 0xfe, 0xe4, 0xf1, 0xe6, 0x70, 0x7f, 0x5a, 0x53, 0x86, 0xb1, 0x49, 0x6b, 0x55, 0xb7, 0xea, 0xd4, + 0x08, 0x8f, 0x2d, 0x0b, 0x38, 0xb4, 0xd1, 0x09, 0x92, 0x0a, 0x64, 0x78, 0x6e, 0xa9, 0x25, 0x0b, + 0x90, 0xa7, 0x61, 0xb0, 0x62, 0x79, 0xd4, 0xd1, 0xeb, 0x9e, 0x79, 0x8f, 0x8a, 0xa9, 0x01, 0x6f, + 0x86, 0xcd, 0x10, 0xac, 0xc9, 0x34, 0xe4, 0x06, 0x0c, 0x2d, 0xe8, 0x8e, 0x67, 0xd6, 0xcd, 0x96, + 0x6e, 0x79, 0xae, 0xd2, 0x8f, 0x33, 0x1a, 0x5a, 0x18, 0x2d, 0x09, 0xae, 0x45, 0xa8, 0xc8, 0x27, + 0x60, 0x00, 0xb7, 0xa6, 0xe8, 0xaf, 0x3d, 0xb0, 0xed, 0xc5, 0xe1, 0x63, 0xa1, 0x7b, 0x20, 0x3f, + 0x7d, 0xc5, 0x1b, 0xe0, 0xf8, 0xdd, 0x61, 0xc0, 0x91, 0x7c, 0x04, 0xfa, 0x26, 0x2d, 0x03, 0x99, + 0xc3, 0xb6, 0xcc, 0x55, 0xc1, 0xfc, 0x4c, 0xc8, 0xdc, 0x6e, 0xc5, 0x78, 0xfb, 0xec, 0xd2, 0x47, + 0xd9, 0xe0, 0xbb, 0x37, 0xca, 0x86, 0xde, 0x85, 0x63, 0xf1, 0xe1, 0x83, 0x3a, 0x16, 0x1f, 0xd9, + 0xe3, 0xb1, 0xb8, 0xfa, 0x36, 0x0c, 0x8e, 0x2f, 0x4c, 0x05, 0xa3, 0xf7, 0x21, 0xc8, 0x2d, 0x08, + 0x4f, 0x85, 0x3c, 0xb7, 0x67, 0x5a, 0xa6, 0xa1, 0x31, 0x18, 0xb9, 0x02, 0xfd, 0x13, 0xe8, 0xfe, + 0x26, 0x6e, 0x11, 0xf3, 0x7c, 0xfd, 0xab, 0x23, 0x0c, 0xbd, 0x60, 0x7d, 0x34, 0x79, 0x1c, 0xfa, + 0x16, 0x1c, 0x7b, 0xc5, 0xd1, 0x9b, 0x62, 0x0d, 0x46, 0x57, 0x91, 0x16, 0x07, 0x69, 0x3e, 0x4e, + 0xfd, 0xd1, 0x8c, 0x6f, 0xb6, 0xb3, 0x12, 0xd5, 0x36, 0x1e, 0xcd, 0x63, 0xdd, 0xfd, 0xbc, 0x84, + 0xcb, 0x41, 0x9a, 0x8f, 0x23, 0x57, 0xa0, 0x77, 0xd2, 0x71, 0x6c, 0x47, 0xf6, 0x71, 0xa7, 0x0c, + 0x20, 0x5f, 0xf7, 0x22, 0x05, 0x79, 0x1e, 0x06, 0xf9, 0x9c, 0xc3, 0x4f, 0x34, 0x73, 0xdd, 0x6e, + 0x4a, 0x65, 0x4a, 0xf5, 0xeb, 0x39, 0xc9, 0x66, 0xe3, 0x12, 0x7f, 0x00, 0x6f, 0x05, 0x9e, 0x81, + 0xdc, 0xf8, 0xc2, 0x94, 0x98, 0x00, 0x4f, 0xfa, 0x45, 0x25, 0x55, 0x89, 0x95, 0x63, 0xd4, 0xe4, + 0x3c, 0xe4, 0x17, 0x98, 0xfa, 0x14, 0x50, 0x3d, 0xfa, 0xb7, 0x36, 0x8b, 0xf9, 0x16, 0xd3, 0x1f, + 0x84, 0x22, 0x96, 0x6d, 0x66, 0xf8, 0x8e, 0x89, 0x63, 0xc3, 0x7d, 0xcc, 0x79, 0xc8, 0x97, 0x9c, + 0x95, 0x7b, 0x62, 0xd6, 0x42, 0xac, 0xee, 0xac, 0xdc, 0xd3, 0x10, 0x4a, 0xae, 0x01, 0x68, 0xd4, + 0x6b, 0x3b, 0x16, 0x3e, 0x3f, 0x19, 0xc0, 0xf3, 0x37, 0x9c, 0x0d, 0x1d, 0x84, 0xd6, 0xea, 0xb6, + 0x41, 0x35, 0x89, 0x44, 0xfd, 0xc5, 0xf0, 0x62, 0xa7, 0x6c, 0xba, 0x6b, 0xc7, 0x5d, 0xb8, 0x8b, + 0x2e, 0xd4, 0xc5, 0x11, 0x67, 0xb2, 0x93, 0x8a, 0xd0, 0x3b, 0xd5, 0xd0, 0x57, 0x5c, 0xec, 0x43, + 0xe1, 0x4b, 0xb6, 0xcc, 0x00, 0x1a, 0x87, 0xc7, 0xfa, 0xa9, 0x7f, 0xfb, 0x7e, 0xfa, 0x72, 0x6f, + 0x30, 0xda, 0xe6, 0xa8, 0xb7, 0x6e, 0x3b, 0xc7, 0x5d, 0xb5, 0xd3, 0xae, 0xba, 0x04, 0x7d, 0x55, + 0xa7, 0x2e, 0x1d, 0x5d, 0xe0, 0x7e, 0xc0, 0x75, 0xea, 0xfc, 0xd8, 0xc2, 0x47, 0x32, 0xba, 0xb2, + 0xeb, 0x21, 0x5d, 0x5f, 0x48, 0x67, 0xb8, 0x9e, 0xa0, 0x13, 0x48, 0x41, 0xb7, 0x60, 0x3b, 0x9e, + 0xe8, 0xb8, 0x80, 0xae, 0x65, 0x3b, 0x9e, 0xe6, 0x23, 0xc9, 0x07, 0x00, 0x16, 0x27, 0x16, 0x7c, + 0x67, 0xfb, 0x81, 0xd0, 0x17, 0x50, 0x78, 0xd9, 0x6b, 0x12, 0x9a, 0x2c, 0xc2, 0xc0, 0x7c, 0x8b, + 0x3a, 0x7c, 0x2b, 0xc4, 0x1f, 0x94, 0xbc, 0x3f, 0x26, 0x5a, 0xd1, 0xef, 0x57, 0xc5, 0xff, 0x01, + 0x39, 0x5f, 0x5f, 0x6c, 0xff, 0xa7, 0x16, 0x32, 0x22, 0xcf, 0x43, 0xa1, 0xc4, 0xed, 0xbc, 0x41, + 0x64, 0x19, 0x88, 0x0c, 0xb7, 0xa0, 0x1c, 0xc5, 0xf7, 0xec, 0x3a, 0xfe, 0xad, 0x09, 0x72, 0xf5, + 0x0a, 0x8c, 0xc6, 0xab, 0x21, 0x83, 0xd0, 0x37, 0x31, 0x3f, 0x37, 0x37, 0x39, 0xb1, 0x38, 0xda, + 0x43, 0xfa, 0x21, 0x5f, 0x9d, 0x9c, 0x2b, 0x8f, 0x66, 0xd4, 0x5f, 0x91, 0x66, 0x10, 0xa6, 0x5a, + 0xc7, 0x57, 0xc3, 0xfb, 0xba, 0x6f, 0x19, 0xc5, 0xfb, 0x50, 0x3c, 0x31, 0x68, 0x9a, 0x9e, 0x47, + 0x0d, 0xb1, 0x4a, 0xe0, 0x7d, 0xa1, 0x77, 0x5f, 0x4b, 0xe0, 0xc9, 0x93, 0x30, 0x8c, 0x30, 0x71, + 0x45, 0xc8, 0xf7, 0xc7, 0xa2, 0x80, 0x73, 0x5f, 0x8b, 0x22, 0xd5, 0x6f, 0x84, 0xb7, 0xc3, 0x33, + 0x54, 0x3f, 0xaa, 0x37, 0x8a, 0xef, 0x91, 0xfe, 0x52, 0xff, 0x36, 0xcf, 0x9f, 0x80, 0xf0, 0xf7, + 0x82, 0x87, 0x21, 0xca, 0xf0, 0x48, 0x37, 0xb7, 0x8b, 0x23, 0xdd, 0x27, 0xa1, 0x30, 0x4b, 0xbd, + 0x55, 0xdb, 0x77, 0xfc, 0x42, 0x0f, 0xbd, 0x26, 0x42, 0x64, 0x0f, 0x3d, 0x4e, 0x43, 0xd6, 0x80, + 0xf8, 0x8f, 0x01, 0x03, 0x47, 0x6c, 0xff, 0x08, 0xf9, 0x6c, 0x62, 0x9f, 0x52, 0xc5, 0x97, 0xc0, + 0xe8, 0x63, 0x7f, 0x2a, 0x70, 0xf4, 0x96, 0x3c, 0xb1, 0xfe, 0x66, 0xb3, 0x58, 0xe0, 0x34, 0x5a, + 0x0a, 0x5b, 0xf2, 0x06, 0x0c, 0xcc, 0x4e, 0x95, 0xc4, 0xc3, 0x40, 0xee, 0x15, 0xf1, 0x50, 0x20, + 0x45, 0x1f, 0x11, 0x88, 0x04, 0xdf, 0xdb, 0x34, 0x97, 0xf5, 0xe4, 0xbb, 0xc0, 0x90, 0x0b, 0xd3, + 0x16, 0xfe, 0x72, 0x47, 0x9c, 0x2e, 0x04, 0xda, 0x12, 0x7d, 0xcf, 0x13, 0x97, 0x15, 0xc7, 0xc6, + 0xb4, 0xa5, 0x7f, 0x1f, 0xa3, 0x7b, 0x1e, 0xc6, 0x4a, 0xad, 0x56, 0xc3, 0xa4, 0x06, 0xea, 0x8b, + 0xd6, 0x6e, 0x50, 0x57, 0xb8, 0xfc, 0xe0, 0x63, 0x10, 0x9d, 0x23, 0x6b, 0xf8, 0x1c, 0xb5, 0xe6, + 0xb4, 0xa3, 0xfe, 0x99, 0xc9, 0xb2, 0xea, 0x8f, 0x67, 0xe1, 0xcc, 0x84, 0x43, 0x75, 0x8f, 0xce, + 0x4e, 0x95, 0x4a, 0x6d, 0xf4, 0x91, 0x6b, 0x34, 0xa8, 0xb5, 0x72, 0x38, 0xc3, 0xfa, 0x25, 0x18, + 0x09, 0x1a, 0x50, 0xad, 0xdb, 0x2d, 0x2a, 0x3f, 0xac, 0xaa, 0xfb, 0x98, 0x9a, 0xcb, 0x50, 0x5a, + 0x8c, 0x94, 0xdc, 0x82, 0x93, 0x01, 0xa4, 0xd4, 0x68, 0xd8, 0xeb, 0x1a, 0x6d, 0xbb, 0xdc, 0x31, + 0xb6, 0x9f, 0x3b, 0xc6, 0x86, 0x1c, 0x74, 0x86, 0xaf, 0x39, 0x8c, 0x40, 0x4b, 0x2b, 0xa5, 0x7e, + 0x25, 0x07, 0x67, 0xef, 0xe8, 0x0d, 0xd3, 0x08, 0x45, 0xa3, 0x51, 0xb7, 0x65, 0x5b, 0x2e, 0x3d, + 0x42, 0xa3, 0x34, 0x32, 0x14, 0xf2, 0x07, 0x32, 0x14, 0x92, 0x5d, 0xd4, 0xbb, 0xef, 0x2e, 0x2a, + 0xec, 0xa9, 0x8b, 0xfe, 0x79, 0x06, 0x46, 0x7d, 0xc7, 0x7f, 0xf9, 0x11, 0xb7, 0xe4, 0x95, 0x8e, + 0x47, 0x88, 0x31, 0x3f, 0x68, 0xc4, 0x93, 0x2a, 0xf4, 0x4d, 0xde, 0x6f, 0x99, 0x0e, 0x75, 0x77, + 0xe0, 0xc4, 0x7d, 0x41, 0x1c, 0x97, 0x8c, 0x51, 0x5e, 0x24, 0x71, 0x52, 0xc2, 0xc1, 0xf8, 0x9c, + 0x8f, 0x3f, 0x7d, 0x18, 0xf7, 0x5f, 0xa6, 0xf3, 0xe7, 0x7c, 0xe2, 0x89, 0x44, 0xe4, 0x7d, 0x66, + 0x48, 0x4a, 0x1e, 0x83, 0xdc, 0xe2, 0xe2, 0x8c, 0x98, 0x49, 0x31, 0x22, 0x80, 0xe7, 0xc9, 0xef, + 0x15, 0x19, 0x56, 0xfd, 0xf3, 0x2c, 0x00, 0x53, 0x05, 0x3e, 0x5c, 0x0f, 0x45, 0x09, 0xc7, 0xa1, + 0xdf, 0x17, 0xb8, 0x50, 0xc3, 0xc0, 0x6b, 0x3f, 0xde, 0x11, 0xf1, 0xba, 0x83, 0x17, 0x1a, 0x45, + 0xdf, 0x91, 0x9c, 0xdf, 0x03, 0xe0, 0xce, 0x06, 0x1d, 0xc9, 0x7d, 0xf7, 0xf1, 0x0f, 0xc0, 0x80, + 0x98, 0xf1, 0xec, 0xc8, 0xf9, 0x7f, 0xdd, 0x07, 0x6a, 0x21, 0x3e, 0x36, 0xb5, 0x16, 0xf6, 0xb1, + 0x10, 0xfb, 0xe2, 0xe5, 0xbd, 0x72, 0x2c, 0xde, 0x03, 0x16, 0xef, 0x17, 0x84, 0x78, 0xf9, 0x0b, + 0x9e, 0x23, 0x2b, 0xde, 0x03, 0x3b, 0xfb, 0x56, 0xff, 0x24, 0x03, 0x84, 0x35, 0x6b, 0x41, 0x77, + 0xdd, 0x75, 0xdb, 0x31, 0xb8, 0x73, 0xfa, 0xa1, 0x08, 0xe6, 0xe0, 0xee, 0x2b, 0xbf, 0xde, 0x0f, + 0x27, 0x23, 0x8e, 0xbf, 0x47, 0x7c, 0xb2, 0xba, 0x12, 0x1d, 0x4d, 0xdd, 0x5e, 0xbd, 0xbc, 0x4f, + 0xbe, 0x10, 0xed, 0x8d, 0x3c, 0x40, 0x93, 0x6e, 0x42, 0x9f, 0x82, 0x21, 0xf1, 0x83, 0xad, 0xd0, + 0xfe, 0x4d, 0x17, 0x8e, 0x52, 0x97, 0x01, 0xb4, 0x08, 0x9a, 0x3c, 0x0b, 0x03, 0x6c, 0xc0, 0xac, + 0x60, 0xf0, 0x90, 0xbe, 0xf0, 0x45, 0x89, 0xe1, 0x03, 0xe5, 0xf5, 0x24, 0xa0, 0x94, 0xde, 0x11, + 0xf5, 0xef, 0xe0, 0x1d, 0xd1, 0x27, 0x61, 0xb0, 0x64, 0x59, 0xb6, 0x87, 0x9b, 0x74, 0x57, 0x5c, + 0x4d, 0x74, 0xb4, 0xca, 0x1f, 0xc3, 0xc7, 0xf1, 0x21, 0x7d, 0xaa, 0x59, 0x2e, 0x33, 0x24, 0xd7, + 0xfd, 0x57, 0x31, 0xd4, 0x11, 0x5e, 0xe5, 0x78, 0x3d, 0xe3, 0x08, 0x58, 0xf2, 0x51, 0x0c, 0x76, + 0xde, 0xf0, 0x82, 0x63, 0xb7, 0x6c, 0x97, 0x1a, 0x5c, 0x50, 0x83, 0x61, 0xa8, 0x81, 0x96, 0x40, + 0xe0, 0x3b, 0xb6, 0x48, 0x20, 0x8f, 0x48, 0x11, 0xb2, 0x0c, 0xa7, 0xfc, 0x8b, 0xe2, 0xe0, 0xc5, + 0x60, 0xa5, 0xec, 0x2a, 0x43, 0xf8, 0x2a, 0x89, 0xc4, 0x95, 0xa1, 0x52, 0x1e, 0xbf, 0xe8, 0x5f, + 0x8b, 0xf8, 0x4f, 0x0e, 0x6b, 0xa6, 0x21, 0x77, 0x75, 0x2a, 0x3f, 0xf2, 0x3d, 0x30, 0x38, 0xab, + 0xdf, 0x2f, 0xb7, 0xc5, 0xd9, 0xcb, 0xf0, 0xce, 0x6f, 0x5f, 0x9a, 0xfa, 0xfd, 0x9a, 0x21, 0xca, + 0xc5, 0x6c, 0x0a, 0x99, 0x25, 0xa9, 0xc1, 0x99, 0x05, 0xc7, 0x6e, 0xda, 0x1e, 0x35, 0x62, 0x8f, + 0xef, 0x4e, 0x84, 0xaf, 0x75, 0x5b, 0x82, 0xa2, 0xd6, 0xe5, 0x15, 0x5e, 0x07, 0x36, 0xa4, 0x09, + 0x27, 0x4a, 0xae, 0xdb, 0x6e, 0xd2, 0xf0, 0x86, 0x6a, 0x74, 0xdb, 0xcf, 0x78, 0xbf, 0xf0, 0x5a, + 0x7e, 0x58, 0xc7, 0xa2, 0xfc, 0x82, 0xaa, 0xe6, 0x99, 0x72, 0x8d, 0xf8, 0x2d, 0x71, 0xde, 0xaf, + 0xe7, 0xfb, 0x47, 0x46, 0x4f, 0x68, 0x67, 0x93, 0x8d, 0x59, 0x34, 0xbd, 0x06, 0x55, 0xbf, 0x96, + 0x01, 0x08, 0x05, 0x4c, 0x9e, 0x8a, 0x46, 0x28, 0xca, 0x84, 0x17, 0x1d, 0x22, 0x7a, 0x41, 0x24, + 0x24, 0x11, 0x39, 0x0f, 0x79, 0x8c, 0x70, 0x91, 0x0d, 0x0f, 0x56, 0xd7, 0x4c, 0xcb, 0xd0, 0x10, + 0xca, 0xb0, 0xd2, 0x53, 0x74, 0xc4, 0xe2, 0xa5, 0x3e, 0xb7, 0x0a, 0xcb, 0x70, 0xa2, 0xda, 0x5e, + 0xf2, 0xeb, 0x96, 0xde, 0xd5, 0x61, 0xa0, 0x0d, 0xb7, 0xbd, 0x14, 0x3c, 0x46, 0x8d, 0x84, 0x31, + 0x89, 0x16, 0x51, 0x7f, 0x2d, 0x13, 0x9b, 0x05, 0x0f, 0x71, 0xd1, 0x7b, 0x5f, 0xd2, 0x4f, 0x23, + 0x39, 0x2d, 0xa9, 0x3f, 0x93, 0x85, 0xc1, 0x05, 0xdb, 0xf1, 0x44, 0xc8, 0x90, 0xa3, 0xbd, 0x0a, + 0x49, 0x7b, 0xa5, 0xfc, 0x2e, 0xf6, 0x4a, 0xe7, 0x21, 0x2f, 0xb9, 0x28, 0xf3, 0x7b, 0x11, 0xc3, + 0x70, 0x34, 0x84, 0xaa, 0xdf, 0x9b, 0x05, 0xf8, 0xc8, 0xd3, 0x4f, 0x3f, 0xc0, 0x02, 0x52, 0x7f, + 0x2a, 0x03, 0x27, 0xc4, 0x45, 0x9d, 0x14, 0xeb, 0xab, 0xcf, 0xbf, 0x62, 0x95, 0xc7, 0x25, 0x07, + 0x69, 0x3e, 0x8e, 0x2d, 0x01, 0x93, 0xf7, 0x4d, 0x0f, 0xef, 0x2a, 0xa4, 0x60, 0x5f, 0x54, 0xc0, + 0xe4, 0x25, 0xc0, 0xa7, 0x23, 0x4f, 0xf9, 0x57, 0x90, 0xb9, 0x70, 0xdd, 0x63, 0x05, 0x26, 0x53, + 0xaf, 0x21, 0xd5, 0xdf, 0xcc, 0x43, 0x7e, 0xf2, 0x3e, 0xad, 0x1f, 0xf1, 0xae, 0x91, 0x0e, 0x36, + 0xf3, 0xfb, 0x3c, 0xd8, 0xdc, 0x8b, 0x4f, 0xc5, 0xab, 0x61, 0x7f, 0x16, 0xa2, 0xd5, 0xc7, 0x7a, + 0x3e, 0x5e, 0xbd, 0xdf, 0xd3, 0x47, 0xcf, 0x25, 0xe7, 0xff, 0xca, 0x41, 0xae, 0x3a, 0xb1, 0x70, + 0xac, 0x37, 0x87, 0xaa, 0x37, 0xdd, 0xef, 0xac, 0xd5, 0xe0, 0x1a, 0xaa, 0x3f, 0xf4, 0x12, 0x8d, + 0xdd, 0x38, 0x7d, 0x3b, 0x07, 0x23, 0xd5, 0xa9, 0xc5, 0x05, 0xe9, 0x24, 0xf8, 0x16, 0xf7, 0xe4, + 0x43, 0x9f, 0x32, 0xde, 0xa5, 0xe7, 0x13, 0xf6, 0xcc, 0xed, 0x8a, 0xe5, 0x3d, 0x77, 0xe3, 0x8e, + 0xde, 0x68, 0x53, 0x3c, 0x7a, 0xe1, 0x7e, 0xbf, 0xae, 0xf9, 0x36, 0xfd, 0x0a, 0x3e, 0xfc, 0xf7, + 0x19, 0x90, 0x97, 0x20, 0x77, 0x5b, 0x78, 0x64, 0x74, 0xe2, 0xf3, 0xcc, 0x75, 0xce, 0x87, 0x4d, + 0x82, 0xb9, 0xb6, 0x69, 0x20, 0x07, 0x56, 0x8a, 0x15, 0xbe, 0x29, 0x16, 0xe0, 0x1d, 0x15, 0x5e, + 0xf1, 0x0b, 0xdf, 0xac, 0x94, 0x49, 0x15, 0x06, 0x17, 0xa8, 0xd3, 0x34, 0xb1, 0xa3, 0xfc, 0x39, + 0xbb, 0x3b, 0x13, 0xb6, 0x53, 0x19, 0x6c, 0x85, 0x85, 0x90, 0x99, 0xcc, 0x85, 0xbc, 0x09, 0xc0, + 0x6d, 0x94, 0x1d, 0xc6, 0x8f, 0xbc, 0x80, 0x76, 0x3f, 0x37, 0x2d, 0x53, 0x6c, 0x3c, 0x89, 0x19, + 0x59, 0x83, 0xd1, 0x59, 0xdb, 0x30, 0x97, 0x4d, 0xee, 0x7a, 0x89, 0x15, 0x14, 0xb6, 0x77, 0x78, + 0x62, 0xa6, 0x64, 0x53, 0x2a, 0x97, 0x56, 0x4d, 0x82, 0xb1, 0xfa, 0xbf, 0xf7, 0x42, 0x9e, 0x75, + 0xfb, 0xf1, 0xf8, 0xdd, 0xcf, 0xf8, 0x2d, 0xc1, 0xe8, 0x5d, 0xdb, 0x59, 0x33, 0xad, 0x95, 0xc0, + 0x2b, 0x5e, 0xec, 0x4d, 0xd1, 0x93, 0x67, 0x9d, 0xe3, 0x6a, 0x81, 0x03, 0xbd, 0x96, 0x20, 0xdf, + 0x66, 0x04, 0xbf, 0x00, 0xc0, 0xdf, 0xba, 0x23, 0x4d, 0x7f, 0x18, 0xac, 0x82, 0xbf, 0x84, 0x47, + 0x47, 0x7b, 0x39, 0x58, 0x45, 0x48, 0xcc, 0x36, 0xe1, 0xdc, 0x17, 0x62, 0x00, 0xfd, 0xee, 0x71, + 0x13, 0x8e, 0xbe, 0x10, 0xb2, 0x11, 0xc0, 0xbd, 0x22, 0x16, 0x00, 0xa4, 0xfb, 0x25, 0x88, 0x09, + 0x22, 0x32, 0x39, 0x88, 0xf0, 0x70, 0x29, 0xd7, 0x4b, 0x9a, 0xc4, 0x83, 0x3c, 0x17, 0xbb, 0x00, + 0x27, 0x11, 0x6e, 0x1d, 0xef, 0xbf, 0x43, 0x07, 0xaa, 0xa1, 0xed, 0x1c, 0xa8, 0xd4, 0xcf, 0x65, + 0x61, 0xa0, 0xda, 0x5e, 0x72, 0x37, 0x5c, 0x8f, 0x36, 0x8f, 0xb8, 0x1a, 0xfb, 0xdb, 0xab, 0x7c, + 0xea, 0xf6, 0xea, 0x31, 0x5f, 0x28, 0xd2, 0xb9, 0x63, 0x60, 0xd2, 0xf9, 0xe2, 0xf8, 0x5f, 0xb3, + 0x30, 0xca, 0x2f, 0xce, 0xca, 0xa6, 0x5b, 0x3f, 0x00, 0x67, 0xfe, 0xc3, 0x97, 0xca, 0xfe, 0x2e, + 0x9b, 0x77, 0xf0, 0x44, 0x42, 0xfd, 0x74, 0x16, 0x06, 0x4b, 0x6d, 0x6f, 0xb5, 0xe4, 0xa1, 0x6e, + 0x3d, 0x90, 0xfb, 0x93, 0x3f, 0xc8, 0xc0, 0x09, 0xd6, 0x90, 0x45, 0x7b, 0x8d, 0x5a, 0x07, 0x70, + 0xf0, 0x28, 0x1f, 0x20, 0x66, 0xf7, 0x78, 0x80, 0xe8, 0xcb, 0x32, 0xb7, 0x3b, 0x59, 0xe2, 0x71, + 0xb9, 0x66, 0x37, 0xe8, 0xd1, 0xfe, 0x8c, 0x03, 0x3c, 0x2e, 0xf7, 0x05, 0x72, 0x00, 0xd7, 0x33, + 0xdf, 0x5d, 0x02, 0x39, 0x80, 0xb3, 0xa5, 0xef, 0x0e, 0x81, 0x7c, 0x3d, 0x03, 0x03, 0xe3, 0xb6, + 0x77, 0xc4, 0x07, 0xbe, 0xf8, 0x8a, 0xa3, 0xad, 0xe6, 0xfe, 0x57, 0x1c, 0x6d, 0xdd, 0x54, 0x7f, + 0x22, 0x0b, 0xa7, 0x44, 0x6c, 0x70, 0x71, 0xfe, 0x70, 0x3c, 0x1d, 0x8b, 0xc1, 0x96, 0x14, 0xcd, + 0xf1, 0x3c, 0x24, 0x44, 0xf3, 0x4b, 0x39, 0x38, 0x85, 0xa1, 0x4c, 0xd9, 0xb6, 0xec, 0xbb, 0xc0, + 0x16, 0x21, 0xf5, 0xe8, 0x25, 0xe8, 0x6c, 0xca, 0x25, 0xe8, 0xdf, 0x6c, 0x16, 0x9f, 0x5b, 0x31, + 0xbd, 0xd5, 0xf6, 0xd2, 0xd5, 0xba, 0xdd, 0xbc, 0xb6, 0xe2, 0xe8, 0xf7, 0x4c, 0x7e, 0xfd, 0xa7, + 0x37, 0xae, 0x05, 0x69, 0x36, 0xf4, 0x96, 0x29, 0x12, 0x70, 0x54, 0x71, 0xaf, 0xc3, 0xb8, 0xfa, + 0xd7, 0xa7, 0x2e, 0xc0, 0xeb, 0xb6, 0x69, 0x09, 0x9f, 0x42, 0x6e, 0xe8, 0x56, 0xd9, 0xfe, 0xf0, + 0x2d, 0xdb, 0xb4, 0x6a, 0x71, 0xc7, 0xc2, 0xdd, 0xd6, 0x17, 0xb2, 0xd6, 0xa4, 0x6a, 0xd4, 0xff, + 0x3f, 0x03, 0x0f, 0x45, 0xb5, 0xf8, 0xbb, 0xc1, 0x76, 0xfc, 0xc9, 0x2c, 0x9c, 0xbe, 0x89, 0xc2, + 0x09, 0x1c, 0x39, 0x8e, 0xe7, 0x2d, 0x31, 0x38, 0x53, 0x64, 0x73, 0x6c, 0x51, 0x76, 0x96, 0xcd, + 0xf1, 0xa4, 0x2e, 0x64, 0xf3, 0x87, 0x19, 0x38, 0x39, 0x5f, 0x29, 0x4f, 0x7c, 0x97, 0x8c, 0xa8, + 0xe4, 0xf7, 0x1c, 0x71, 0x83, 0x33, 0xf1, 0x3d, 0x47, 0xdc, 0xf4, 0xfc, 0x52, 0x16, 0x4e, 0x56, + 0x4b, 0xb3, 0x33, 0xdf, 0x2d, 0x33, 0xf8, 0x84, 0xec, 0x75, 0xe8, 0x1f, 0x82, 0x09, 0x5b, 0x40, + 0xfe, 0xcc, 0x3b, 0xd7, 0x3b, 0x7b, 0x23, 0x26, 0x85, 0x72, 0xc4, 0xa7, 0xee, 0x03, 0x11, 0x0a, + 0xd3, 0xfc, 0x08, 0xf5, 0x11, 0xd7, 0xfc, 0xff, 0xb3, 0x00, 0x83, 0xb7, 0xda, 0x4b, 0x54, 0x38, + 0xa7, 0x3c, 0xd0, 0x27, 0xbf, 0xd7, 0x61, 0x50, 0x88, 0x01, 0x6f, 0x4d, 0xa4, 0xe0, 0x79, 0x22, + 0x18, 0x0a, 0x8f, 0x4f, 0x24, 0x13, 0x91, 0xf3, 0x90, 0xbf, 0x43, 0x9d, 0x25, 0xf9, 0x5d, 0xe9, + 0x3d, 0xea, 0x2c, 0x69, 0x08, 0x25, 0x33, 0xa1, 0xcb, 0x7c, 0x69, 0xa1, 0x82, 0x89, 0x54, 0xc4, + 0x85, 0x0d, 0x66, 0x86, 0x09, 0xfc, 0xde, 0xf4, 0x96, 0xc9, 0x53, 0xb0, 0xc8, 0x6f, 0xda, 0xe3, + 0x25, 0xc9, 0x1c, 0x8c, 0xc9, 0x8e, 0x4f, 0x3c, 0x8b, 0x48, 0x7f, 0x0a, 0xbb, 0xb4, 0xfc, 0x21, + 0xc9, 0xa2, 0xe4, 0x55, 0x18, 0xf2, 0x81, 0xe8, 0xc2, 0x35, 0x10, 0x86, 0xae, 0x0f, 0x58, 0xc5, + 0x52, 0x14, 0x45, 0x0a, 0xc8, 0x0c, 0xf0, 0x1a, 0x02, 0x52, 0x18, 0xc4, 0x5c, 0xe2, 0x22, 0x05, + 0xc8, 0xb3, 0xc8, 0x00, 0x9f, 0x79, 0xa0, 0xb3, 0xca, 0x20, 0x3e, 0xba, 0x44, 0x97, 0x7c, 0x47, + 0xc0, 0xf9, 0xd3, 0xda, 0x08, 0x19, 0x99, 0x07, 0x08, 0x9d, 0x0a, 0x44, 0x00, 0x83, 0x5d, 0xbb, + 0x3b, 0x48, 0x2c, 0xe4, 0xeb, 0xc0, 0xe1, 0xbd, 0x5c, 0x07, 0xaa, 0x7f, 0x9c, 0x85, 0xc1, 0x52, + 0xab, 0x15, 0x0c, 0x85, 0xa7, 0xa0, 0x50, 0x6a, 0xb5, 0x6e, 0x6b, 0x15, 0x39, 0x94, 0xb9, 0xde, + 0x6a, 0xd5, 0xda, 0x8e, 0x29, 0xfb, 0x84, 0x72, 0x22, 0x32, 0x01, 0xc3, 0xa5, 0x56, 0x6b, 0xa1, + 0xbd, 0xd4, 0x30, 0xeb, 0x52, 0x66, 0x24, 0x9e, 0x3b, 0xae, 0xd5, 0xaa, 0xb5, 0x10, 0x13, 0x4f, + 0x8f, 0x15, 0x2d, 0x43, 0x3e, 0x89, 0x61, 0x7f, 0x44, 0x62, 0x1e, 0x9e, 0xfa, 0x43, 0x0d, 0x82, + 0x98, 0x87, 0x6d, 0xbb, 0x1a, 0x10, 0xf1, 0x60, 0xef, 0xe7, 0xfd, 0x90, 0xf9, 0xac, 0xa2, 0x44, + 0x02, 0x9e, 0x90, 0x25, 0xf9, 0x20, 0xf4, 0x95, 0x5a, 0x2d, 0xe9, 0xbe, 0x09, 0x9d, 0x8a, 0x58, + 0xa9, 0x78, 0xee, 0x33, 0x41, 0x76, 0xee, 0x65, 0x18, 0x89, 0x56, 0xb6, 0xab, 0x60, 0xf1, 0xdf, + 0xc9, 0xe0, 0x07, 0x1d, 0x71, 0x9f, 0xe6, 0x67, 0x20, 0x57, 0x6a, 0xb5, 0xc4, 0x7c, 0x74, 0x32, + 0xa5, 0x3f, 0xe2, 0x4f, 0xa0, 0x4b, 0xad, 0x96, 0xff, 0xe9, 0x47, 0xfc, 0x71, 0xc4, 0x9e, 0x3e, + 0xfd, 0xeb, 0xfc, 0xd3, 0x8f, 0xf6, 0xc3, 0x05, 0xf5, 0x37, 0x73, 0x70, 0xa2, 0xd4, 0x6a, 0x1d, + 0x07, 0x99, 0x3f, 0xa8, 0x87, 0xd6, 0x4f, 0x03, 0x48, 0xd3, 0x63, 0x5f, 0xf0, 0x74, 0x6b, 0x50, + 0x9a, 0x1a, 0x95, 0x8c, 0x26, 0x11, 0xf9, 0xea, 0xd7, 0xbf, 0x2b, 0xf5, 0xfb, 0x74, 0x0e, 0xa7, + 0xe2, 0xa3, 0x1e, 0x34, 0xea, 0xbd, 0xd2, 0x6d, 0xa2, 0x0f, 0x0a, 0xbb, 0xea, 0x83, 0xdf, 0x8f, + 0x0c, 0x1e, 0x0c, 0x5a, 0x7e, 0xdc, 0x0b, 0xbd, 0xfb, 0x32, 0x8b, 0x47, 0x64, 0x61, 0x8a, 0x48, + 0x36, 0x7e, 0x22, 0x25, 0x11, 0x57, 0xa9, 0xce, 0x50, 0x35, 0xd3, 0xd0, 0x62, 0xb4, 0x7e, 0x1f, + 0xf6, 0xed, 0xaa, 0x0f, 0x37, 0xb3, 0xf8, 0x76, 0x3a, 0x88, 0xcb, 0xb4, 0xff, 0xdd, 0xc5, 0x35, + 0x00, 0xee, 0x79, 0x10, 0xb8, 0x35, 0x0f, 0xf3, 0x10, 0x2c, 0x3c, 0xbf, 0x92, 0x08, 0xc1, 0x12, + 0x92, 0x04, 0x1e, 0x52, 0xb9, 0x54, 0x0f, 0xa9, 0x2b, 0xd0, 0xaf, 0xe9, 0xeb, 0x6f, 0xb4, 0xa9, + 0xb3, 0x21, 0xcc, 0x19, 0x1e, 0xf6, 0x50, 0x5f, 0xaf, 0x7d, 0x8a, 0x01, 0xb5, 0x00, 0x4d, 0xd4, + 0xe0, 0xf1, 0xbd, 0xe4, 0x11, 0xc2, 0xcf, 0xc8, 0x83, 0x27, 0xf7, 0x7b, 0x51, 0x74, 0xf2, 0x22, + 0xe4, 0x4a, 0x77, 0xab, 0x42, 0xb2, 0x41, 0xd7, 0x96, 0xee, 0x56, 0x85, 0xbc, 0x3a, 0x96, 0xbd, + 0x5b, 0x55, 0x3f, 0x9d, 0x05, 0x92, 0xa4, 0x24, 0xcf, 0xc1, 0x00, 0x42, 0x57, 0x98, 0xce, 0xc8, + 0x89, 0x39, 0xd7, 0xdd, 0x9a, 0x83, 0xd0, 0x88, 0x71, 0xe7, 0x93, 0x92, 0x17, 0x30, 0xf5, 0xb1, + 0x48, 0x0d, 0x17, 0x49, 0xcc, 0xb9, 0xee, 0xfa, 0xc9, 0x82, 0x63, 0x99, 0x8f, 0x05, 0x31, 0xda, + 0x85, 0x77, 0xab, 0xd3, 0xb6, 0xeb, 0x09, 0x51, 0x73, 0xbb, 0x70, 0xdd, 0xc5, 0x8c, 0xb0, 0x11, + 0xbb, 0x90, 0x93, 0x61, 0x56, 0xab, 0xbb, 0x55, 0xfe, 0x4c, 0xc5, 0xd0, 0xec, 0x86, 0x6f, 0x50, + 0xf2, 0xac, 0x56, 0xeb, 0x6e, 0x8d, 0x3f, 0x71, 0x31, 0x30, 0xe7, 0x72, 0x24, 0xab, 0x55, 0xa4, + 0x94, 0xfa, 0xf9, 0x7e, 0x18, 0x2d, 0xeb, 0x9e, 0xbe, 0xa4, 0xbb, 0x54, 0xda, 0x4d, 0x9f, 0xf0, + 0x61, 0xfe, 0xe7, 0x48, 0x72, 0x30, 0x96, 0x52, 0xbe, 0x26, 0x5e, 0x80, 0xbc, 0x14, 0xf2, 0x0d, + 0x72, 0x8e, 0xca, 0x49, 0xcc, 0x96, 0x6a, 0x2d, 0x01, 0xd6, 0x12, 0x84, 0xe4, 0x49, 0x18, 0xf4, + 0x61, 0x6c, 0x03, 0x90, 0x0b, 0x75, 0xc6, 0x58, 0x62, 0xf6, 0xbf, 0x26, 0xa3, 0xc9, 0x0b, 0x30, + 0xe4, 0xff, 0x94, 0x4c, 0x6b, 0x9e, 0x91, 0x6d, 0x29, 0xb1, 0x7b, 0x92, 0x49, 0xe5, 0xa2, 0x38, + 0xbf, 0xf5, 0x46, 0x8a, 0xc6, 0x92, 0x9e, 0x45, 0x48, 0xc9, 0xa7, 0x60, 0xc4, 0xff, 0x2d, 0x36, + 0x0c, 0x3c, 0x3f, 0xdc, 0x93, 0x41, 0x4a, 0xe7, 0x98, 0x58, 0xaf, 0x46, 0xc9, 0xf9, 0xd6, 0xe1, + 0x61, 0x3f, 0x8f, 0x97, 0xb1, 0x94, 0xdc, 0x39, 0xc4, 0x2a, 0x20, 0x15, 0x18, 0xf3, 0x21, 0xa1, + 0x86, 0xf6, 0x85, 0x3b, 0x46, 0x63, 0xa9, 0x96, 0xaa, 0xa4, 0xc9, 0x52, 0xa4, 0x01, 0xe7, 0x23, + 0x40, 0xc3, 0x5d, 0x35, 0x97, 0x3d, 0xb1, 0xdd, 0x13, 0x31, 0x88, 0x45, 0xe2, 0xc6, 0x80, 0x2b, + 0xa7, 0xf1, 0x33, 0xb0, 0x46, 0xb3, 0x43, 0x75, 0xe5, 0x46, 0xaa, 0x70, 0xca, 0xc7, 0xdf, 0x9c, + 0x58, 0x58, 0x70, 0xec, 0xb7, 0x68, 0xdd, 0xab, 0x94, 0xc5, 0x76, 0x19, 0x63, 0xd3, 0x19, 0x4b, + 0xb5, 0x95, 0x7a, 0x8b, 0x29, 0x05, 0xc3, 0x45, 0x99, 0xa7, 0x16, 0x26, 0x77, 0xe0, 0xb4, 0x04, + 0x97, 0xd2, 0x43, 0x43, 0xb8, 0x9f, 0x17, 0x5c, 0xd3, 0x33, 0x44, 0xa7, 0x17, 0x27, 0x2f, 0xc3, + 0xb0, 0x8f, 0xe0, 0xb7, 0x88, 0x83, 0x78, 0x8b, 0x88, 0x43, 0xd2, 0x58, 0xaa, 0xc5, 0x5f, 0x53, + 0x46, 0x89, 0x65, 0x8d, 0xc2, 0x8c, 0xfa, 0x43, 0x11, 0x8d, 0xf2, 0x36, 0x5a, 0xa9, 0xca, 0x88, + 0x59, 0xf6, 0x5f, 0x0d, 0x35, 0x6a, 0xde, 0x31, 0x57, 0x4c, 0xbe, 0x93, 0xf6, 0x1f, 0x50, 0x2e, + 0xd5, 0x6c, 0x04, 0xa6, 0xe9, 0x07, 0x27, 0x3f, 0x57, 0x82, 0x93, 0x29, 0x3a, 0xb6, 0xab, 0x1d, + 0xe3, 0xe7, 0xb2, 0x61, 0x23, 0x8e, 0xf8, 0xb6, 0x71, 0x1c, 0xfa, 0xfd, 0x2f, 0x11, 0xc6, 0x83, + 0xd2, 0x69, 0x68, 0xc6, 0x79, 0xf8, 0xf8, 0x88, 0x38, 0x8e, 0xf8, 0x56, 0xf2, 0x20, 0xc4, 0xf1, + 0x4e, 0x26, 0x14, 0xc7, 0x11, 0xdf, 0x5e, 0xfe, 0x61, 0x2e, 0x9c, 0x93, 0x8e, 0xf7, 0x98, 0x07, + 0x65, 0x26, 0x87, 0x7e, 0xb0, 0x85, 0x5d, 0x3c, 0x64, 0x94, 0x55, 0xb3, 0x6f, 0x8f, 0xaa, 0xf9, + 0xa7, 0xc9, 0xfe, 0xe4, 0xa6, 0xe7, 0x91, 0xec, 0xcf, 0x03, 0x18, 0xac, 0xe4, 0x7a, 0xb8, 0x8e, + 0x71, 0x1b, 0xbd, 0x57, 0x0a, 0xf1, 0xb7, 0x24, 0x4c, 0xf4, 0x28, 0x09, 0xf9, 0x18, 0x9c, 0x8d, + 0x00, 0x16, 0x74, 0x47, 0x6f, 0x52, 0x2f, 0xcc, 0x38, 0x88, 0x41, 0x9b, 0xfc, 0xd2, 0xb5, 0x56, + 0x80, 0x96, 0xb3, 0x18, 0x76, 0xe0, 0x20, 0x29, 0x47, 0xdf, 0x2e, 0x9c, 0xa4, 0xbf, 0x9c, 0x0b, + 0x4d, 0x95, 0x68, 0xf0, 0x55, 0x8d, 0xba, 0xed, 0x86, 0xf7, 0xe0, 0x76, 0xf0, 0xde, 0x52, 0x5b, + 0x4c, 0xc3, 0x89, 0xd2, 0xf2, 0x32, 0xad, 0x7b, 0x7e, 0x4c, 0x69, 0x57, 0x84, 0xdb, 0xe3, 0x5b, + 0x07, 0x81, 0x12, 0x31, 0x82, 0x23, 0xb9, 0xf1, 0x63, 0xc5, 0xd4, 0x3f, 0xcb, 0x83, 0x12, 0x98, + 0xee, 0xc1, 0x43, 0xad, 0x43, 0x5c, 0x26, 0xdf, 0x13, 0xbd, 0x62, 0xc2, 0x58, 0x28, 0x8c, 0x6a, + 0xbb, 0xd9, 0xd4, 0x71, 0xe8, 0xb1, 0xad, 0x41, 0x31, 0xce, 0x2c, 0x24, 0xe4, 0xbb, 0x81, 0x73, + 0x62, 0x37, 0x40, 0xc2, 0x87, 0x70, 0x35, 0x97, 0xb3, 0xd0, 0x92, 0x5c, 0xc9, 0x17, 0x32, 0x70, + 0xca, 0xef, 0x94, 0xf9, 0x25, 0x66, 0x16, 0x4f, 0xd8, 0x6d, 0xcb, 0xf3, 0x77, 0x22, 0x2f, 0x76, + 0xae, 0x8e, 0x77, 0xd2, 0xd5, 0xb4, 0xc2, 0xbc, 0x25, 0x41, 0x60, 0x89, 0x40, 0x21, 0x6c, 0xa4, + 0xa9, 0xd5, 0x91, 0x48, 0x4b, 0xad, 0xf7, 0xdc, 0x4d, 0x78, 0xa8, 0x23, 0xcb, 0xed, 0xcc, 0xd0, + 0x5e, 0xd9, 0x0c, 0xfd, 0xfb, 0x99, 0x70, 0x22, 0x8a, 0x09, 0x89, 0x5c, 0x05, 0x08, 0x41, 0x62, + 0x63, 0x3a, 0xb2, 0xb5, 0x59, 0x84, 0x50, 0x68, 0x9a, 0x44, 0x41, 0xe6, 0xa1, 0x20, 0xc4, 0xc2, + 0xb3, 0xfb, 0x7e, 0x60, 0x9b, 0x5e, 0xb8, 0x2a, 0xcb, 0x01, 0x37, 0x9d, 0xe2, 0x9b, 0x05, 0x9b, + 0x73, 0x2f, 0xc0, 0xe0, 0x5e, 0xbf, 0xeb, 0x0b, 0x39, 0x20, 0xf2, 0x2e, 0xf2, 0x10, 0x4d, 0xec, + 0x23, 0x3c, 0x85, 0x5d, 0x86, 0x7e, 0xf6, 0x09, 0x98, 0xef, 0x42, 0x8a, 0x6f, 0xdb, 0x16, 0x30, + 0x2d, 0xc0, 0x86, 0xc1, 0xa5, 0xfa, 0xd2, 0x83, 0x4b, 0xa9, 0x3f, 0x96, 0x83, 0x33, 0x72, 0x87, + 0x94, 0x29, 0x86, 0xcc, 0x3f, 0xee, 0x94, 0x77, 0xb1, 0x53, 0x54, 0x28, 0xf0, 0xcd, 0x83, 0xc8, + 0x5d, 0xc0, 0x0f, 0x76, 0x10, 0xa2, 0x09, 0x8c, 0xfa, 0x4f, 0xb2, 0x30, 0xbc, 0x60, 0xbb, 0xde, + 0x8a, 0x43, 0xdd, 0x05, 0xdd, 0x71, 0x1f, 0xe0, 0xee, 0xf8, 0x10, 0x0c, 0x63, 0x78, 0xa0, 0x26, + 0xb5, 0x78, 0x08, 0x9d, 0x5e, 0x29, 0xd9, 0x88, 0x8f, 0x10, 0x79, 0xa5, 0x22, 0x84, 0x4c, 0xfb, + 0xb9, 0xe5, 0x27, 0x05, 0x6d, 0xe2, 0x66, 0x1f, 0x87, 0xab, 0x3f, 0x97, 0x83, 0x21, 0x5f, 0xca, + 0xe3, 0xe6, 0x51, 0xbd, 0xa9, 0x39, 0x5c, 0x21, 0x5f, 0x03, 0x58, 0xb0, 0x1d, 0x4f, 0x6f, 0xcc, + 0x85, 0x9a, 0x8f, 0x47, 0x9c, 0x2d, 0x84, 0xf2, 0x32, 0x12, 0x09, 0xae, 0x5f, 0xa1, 0x59, 0xcd, + 0x27, 0x26, 0xbe, 0x7e, 0x05, 0x50, 0x4d, 0xa2, 0x50, 0x7f, 0x37, 0x0b, 0x27, 0xfc, 0x4e, 0x9a, + 0xbc, 0x4f, 0xeb, 0xed, 0x07, 0x79, 0x6e, 0x8a, 0x4a, 0xbb, 0x77, 0x5b, 0x69, 0xab, 0xff, 0x5a, + 0x9a, 0x48, 0x26, 0x1a, 0xf6, 0xf1, 0x44, 0xf2, 0x77, 0xa1, 0xe3, 0xea, 0xf7, 0xe7, 0xe0, 0x94, + 0x2f, 0xf5, 0xa9, 0xb6, 0x85, 0x87, 0x03, 0x13, 0x7a, 0xa3, 0xf1, 0x20, 0xef, 0xc6, 0x07, 0x7d, + 0x41, 0xcc, 0x8b, 0x78, 0x7b, 0x22, 0xc7, 0xdf, 0xb2, 0x00, 0xd7, 0x6c, 0xd3, 0xd0, 0x64, 0x22, + 0xf2, 0x2a, 0x0c, 0xf9, 0x3f, 0x4b, 0xce, 0x8a, 0xbf, 0x05, 0xc7, 0xa3, 0xfe, 0xa0, 0x90, 0xee, + 0x44, 0xc2, 0x0a, 0x44, 0x0a, 0xa8, 0xff, 0xac, 0x00, 0xe7, 0xee, 0x9a, 0x96, 0x61, 0xaf, 0xbb, + 0x7e, 0x8a, 0xc8, 0x23, 0x7f, 0xd4, 0x75, 0xd8, 0xa9, 0x21, 0xdf, 0x80, 0xd3, 0x71, 0x91, 0x3a, + 0x41, 0xe0, 0x6e, 0xd1, 0x3b, 0xeb, 0x9c, 0xa0, 0xe6, 0x27, 0x8b, 0x14, 0xf7, 0x65, 0x5a, 0x7a, + 0xc9, 0x78, 0xb6, 0xc9, 0xbe, 0x9d, 0x64, 0x9b, 0x7c, 0x02, 0x0a, 0x65, 0xbb, 0xa9, 0x9b, 0x7e, + 0x80, 0x19, 0x1c, 0xc5, 0x41, 0xbd, 0x88, 0xd1, 0x04, 0x05, 0xe3, 0x2f, 0x2a, 0xc6, 0x2e, 0x1b, + 0x08, 0xf9, 0xfb, 0x05, 0x98, 0x95, 0xa6, 0xc9, 0x44, 0xc4, 0x86, 0x61, 0x51, 0x9d, 0xb8, 0xdd, + 0x02, 0xdc, 0x3c, 0x3d, 0xeb, 0xcb, 0xa8, 0xb3, 0x5a, 0x5d, 0x8d, 0x94, 0xe3, 0xdb, 0x28, 0x9e, + 0x04, 0x53, 0x7c, 0x0c, 0xbf, 0xe7, 0xd2, 0xa2, 0xfc, 0x25, 0x21, 0xe0, 0x24, 0x33, 0x98, 0x14, + 0x02, 0xce, 0x32, 0x32, 0x11, 0x99, 0x84, 0x31, 0x0c, 0xaf, 0x1c, 0x6c, 0xa5, 0x98, 0x4a, 0x0c, + 0xa1, 0x51, 0x89, 0x97, 0x26, 0x3c, 0x22, 0x33, 0xfb, 0xb8, 0x5a, 0x5d, 0xa0, 0xb5, 0x64, 0x89, + 0x73, 0xaf, 0x01, 0x49, 0xb6, 0x79, 0x57, 0xd7, 0x26, 0xff, 0xb7, 0xb4, 0xaf, 0x3b, 0xea, 0x8e, + 0x2f, 0x07, 0x31, 0xdb, 0x45, 0x52, 0x87, 0xf5, 0xbe, 0x9b, 0xa9, 0xc3, 0x0a, 0x07, 0x9a, 0x3a, + 0x4c, 0xfd, 0xd5, 0x0c, 0x8c, 0x25, 0xe2, 0x8c, 0x93, 0x67, 0x00, 0x38, 0x44, 0x8a, 0xe7, 0x88, + 0x01, 0x52, 0xc2, 0xd8, 0xe3, 0x62, 0x0d, 0x0c, 0xc9, 0xc8, 0x35, 0xe8, 0xe7, 0xbf, 0x44, 0x0c, + 0xa6, 0x64, 0x91, 0x76, 0xdb, 0x34, 0xb4, 0x80, 0x28, 0xac, 0x05, 0x2f, 0x0e, 0x73, 0xa9, 0x45, + 0xbc, 0x8d, 0x56, 0x50, 0x0b, 0x23, 0x53, 0x3f, 0x9f, 0x85, 0xa1, 0xa0, 0xc1, 0x25, 0xe3, 0xb0, + 0x74, 0xae, 0x20, 0x42, 0xb6, 0xe7, 0xb6, 0x0b, 0xd9, 0x1e, 0x9b, 0x54, 0x45, 0x8c, 0xf6, 0x83, + 0x7b, 0xf7, 0xf4, 0xc5, 0x2c, 0x9c, 0x08, 0x6a, 0x3d, 0xc4, 0x3b, 0xaa, 0xf7, 0x90, 0x48, 0xbe, + 0x90, 0x01, 0x65, 0xdc, 0x6c, 0x34, 0x4c, 0x6b, 0xa5, 0x62, 0x2d, 0xdb, 0x4e, 0x13, 0x67, 0xbd, + 0xc3, 0x3b, 0xa7, 0x55, 0x7f, 0x28, 0x03, 0x63, 0xa2, 0x41, 0x13, 0xba, 0x63, 0x1c, 0xde, 0x21, + 0x58, 0xbc, 0x25, 0x87, 0xa7, 0x2f, 0xea, 0x57, 0xb3, 0x00, 0x33, 0x76, 0x7d, 0xed, 0x88, 0x3f, + 0x9b, 0x7a, 0x09, 0x0a, 0x3c, 0x10, 0x96, 0xd0, 0xd8, 0x31, 0xf1, 0x3c, 0x88, 0x7d, 0x1a, 0x47, + 0x8c, 0x8f, 0x8a, 0xf9, 0xb8, 0xc0, 0x03, 0x69, 0x29, 0x19, 0x4d, 0x14, 0x61, 0x95, 0x32, 0x3a, + 0xb1, 0x60, 0x04, 0x95, 0x32, 0x58, 0xb4, 0xd2, 0xad, 0xcd, 0x62, 0xbe, 0x61, 0xd7, 0xd7, 0x34, + 0xa4, 0x57, 0xff, 0x36, 0xc3, 0x65, 0x77, 0xc4, 0x1f, 0x7f, 0xfa, 0x9f, 0x9f, 0xdf, 0xe5, 0xe7, + 0xff, 0x70, 0x06, 0x4e, 0x69, 0xb4, 0x6e, 0xdf, 0xa3, 0xce, 0xc6, 0x84, 0x6d, 0xd0, 0x9b, 0xd4, + 0xa2, 0xce, 0x61, 0x8d, 0xa8, 0xff, 0x0d, 0x73, 0x5c, 0x84, 0x8d, 0xb9, 0xed, 0x52, 0xe3, 0xe8, + 0xe4, 0x1f, 0x51, 0x7f, 0xa3, 0x0f, 0x94, 0x54, 0xd3, 0xf6, 0xc8, 0x9a, 0x73, 0x1d, 0xf7, 0x2b, + 0xf9, 0x83, 0xda, 0xaf, 0xf4, 0xee, 0x6e, 0xbf, 0x52, 0xd8, 0xed, 0x7e, 0xa5, 0x6f, 0x27, 0xfb, + 0x95, 0x66, 0x7c, 0xbf, 0xd2, 0x8f, 0xfb, 0x95, 0x67, 0xba, 0xee, 0x57, 0x26, 0x2d, 0x63, 0x8f, + 0xbb, 0x95, 0x23, 0x9b, 0x1b, 0x77, 0x2f, 0xdb, 0xac, 0xcb, 0x6c, 0x52, 0xac, 0xdb, 0x8e, 0x41, + 0x0d, 0xb1, 0xbb, 0xc2, 0xa3, 0x7d, 0x47, 0xc0, 0xb4, 0x00, 0x9b, 0x48, 0x34, 0x3c, 0xbc, 0x93, + 0x44, 0xc3, 0x07, 0xb0, 0xff, 0xfa, 0x5c, 0x16, 0xc6, 0x26, 0xa8, 0xe3, 0xf1, 0x48, 0x9b, 0x07, + 0xe1, 0xb9, 0x56, 0x82, 0x13, 0x12, 0x43, 0xb4, 0xc8, 0xb3, 0xa1, 0x37, 0x5e, 0x9d, 0x3a, 0x5e, + 0xdc, 0x99, 0x2f, 0x4e, 0xcf, 0xaa, 0xf7, 0x93, 0x7d, 0x89, 0xb1, 0x1b, 0x54, 0xef, 0xc3, 0xb9, + 0x20, 0x4d, 0xf1, 0x4b, 0x0b, 0xe8, 0xa5, 0xfc, 0x5d, 0xf9, 0xdd, 0xe7, 0xef, 0x52, 0x7f, 0x25, + 0x03, 0x97, 0x34, 0x6a, 0xd1, 0x75, 0x7d, 0xa9, 0x41, 0xa5, 0x66, 0x89, 0x95, 0x81, 0xcd, 0x1a, + 0xa6, 0xdb, 0xd4, 0xbd, 0xfa, 0xea, 0xbe, 0x64, 0x34, 0x05, 0x43, 0xf2, 0xfc, 0xb5, 0x8b, 0xb9, + 0x2d, 0x52, 0x4e, 0xfd, 0x8d, 0x3c, 0xf4, 0x8d, 0xdb, 0xde, 0xeb, 0xf6, 0x3e, 0x13, 0xca, 0x85, + 0x53, 0x7e, 0x76, 0x17, 0x07, 0x3a, 0x1f, 0xc4, 0xca, 0xa5, 0x18, 0xfb, 0xe8, 0xe9, 0xb9, 0x64, + 0x27, 0x72, 0x11, 0xf8, 0x64, 0xbb, 0x4c, 0x25, 0xf7, 0x1c, 0x0c, 0x60, 0x90, 0x16, 0xe9, 0xc8, + 0x15, 0xfd, 0xa8, 0x3d, 0x06, 0x8c, 0xd7, 0x11, 0x92, 0x92, 0x8f, 0x45, 0x42, 0x83, 0x16, 0xf6, + 0x9f, 0x7a, 0x4e, 0x8e, 0x12, 0xfa, 0x0c, 0xbf, 0xad, 0xc3, 0x36, 0x49, 0x69, 0x3a, 0xf0, 0xa8, + 0x24, 0xd6, 0xa4, 0x80, 0xf0, 0x00, 0xd3, 0xc2, 0x4d, 0xc0, 0xf0, 0xb8, 0xed, 0x49, 0x3e, 0xbb, + 0x03, 0xe1, 0x6b, 0x4d, 0x26, 0xf9, 0x74, 0x87, 0xdd, 0x68, 0x19, 0xf5, 0xdb, 0x79, 0x18, 0xf2, + 0x7f, 0x1e, 0x92, 0xee, 0x3c, 0x05, 0x85, 0x69, 0x5b, 0xca, 0x54, 0x80, 0x7e, 0xbe, 0xab, 0xb6, + 0x1b, 0x73, 0x60, 0x16, 0x44, 0x4c, 0xea, 0x73, 0xb6, 0x21, 0x7b, 0xa9, 0xa3, 0xd4, 0x2d, 0xdb, + 0x48, 0xbc, 0xf2, 0x0d, 0x08, 0xc9, 0x25, 0xc8, 0xa3, 0x83, 0xbf, 0x74, 0x5a, 0x1f, 0x73, 0xea, + 0x47, 0xbc, 0xa4, 0x95, 0x85, 0xdd, 0x6a, 0x65, 0xdf, 0x5e, 0xb5, 0xb2, 0xff, 0x60, 0xb5, 0xf2, + 0x4d, 0x18, 0xc2, 0x9a, 0xfc, 0x44, 0x67, 0xdb, 0x2f, 0xac, 0x0f, 0x89, 0xb5, 0x6f, 0x98, 0xb7, + 0x5b, 0xa4, 0x3b, 0xc3, 0x25, 0x2f, 0xc2, 0x2a, 0xa6, 0xbb, 0xb0, 0x8f, 0xed, 0xf4, 0x9f, 0x66, + 0xa0, 0xef, 0xb6, 0xb5, 0x66, 0xd9, 0xeb, 0xfb, 0xd3, 0xb8, 0x67, 0x60, 0x50, 0xb0, 0x91, 0x56, + 0x17, 0x7c, 0xb8, 0xdd, 0xe6, 0xe0, 0x1a, 0x72, 0xd2, 0x64, 0x2a, 0xf2, 0x72, 0x50, 0x08, 0xdf, + 0xf0, 0xe4, 0xc2, 0x5c, 0x1f, 0x7e, 0xa1, 0x7a, 0x34, 0x3d, 0x81, 0x4c, 0x4e, 0xce, 0x43, 0xbe, + 0xcc, 0x9a, 0x2a, 0x05, 0xbb, 0x65, 0x4d, 0xd1, 0x10, 0xaa, 0x7e, 0x2e, 0x0f, 0x23, 0xb1, 0x83, + 0xaf, 0x27, 0x60, 0x40, 0x1c, 0x3c, 0x99, 0x7e, 0xbe, 0x04, 0x7c, 0xe3, 0x13, 0x00, 0xb5, 0x7e, + 0xfe, 0x67, 0xc5, 0x20, 0x1f, 0x86, 0x3e, 0xdb, 0xc5, 0x45, 0x11, 0xbf, 0x65, 0x24, 0x1c, 0x42, + 0xf3, 0x55, 0xd6, 0x76, 0x3e, 0x38, 0x04, 0x89, 0xac, 0x91, 0xb6, 0x8b, 0x9f, 0x76, 0x03, 0x06, + 0x74, 0xd7, 0xa5, 0x5e, 0xcd, 0xd3, 0x57, 0xe4, 0x14, 0x0a, 0x01, 0x50, 0x1e, 0x1d, 0x08, 0x5c, + 0xd4, 0x57, 0xc8, 0x6b, 0x30, 0x5c, 0x77, 0x28, 0x2e, 0x9b, 0x7a, 0x83, 0xb5, 0x52, 0x32, 0x6b, + 0x23, 0x08, 0xf9, 0x92, 0x24, 0x44, 0x54, 0x0c, 0x72, 0x07, 0x86, 0xc5, 0xe7, 0x70, 0x07, 0x7b, + 0x1c, 0x68, 0x23, 0xe1, 0x32, 0xc6, 0x45, 0xc2, 0x5d, 0xec, 0xc5, 0x3b, 0x0b, 0x99, 0x5c, 0xe6, + 0x6b, 0x48, 0xa4, 0x64, 0x1e, 0xc8, 0x3a, 0x5d, 0xaa, 0xe9, 0x6d, 0x6f, 0x95, 0xd5, 0xc5, 0x23, + 0x80, 0x8b, 0xcc, 0x81, 0xf8, 0x38, 0x21, 0x89, 0x95, 0xdf, 0x6c, 0xac, 0xd3, 0xa5, 0x52, 0x04, + 0x49, 0xee, 0xc2, 0xe9, 0x64, 0x11, 0xf6, 0xc9, 0xfc, 0x06, 0xe0, 0xb1, 0xad, 0xcd, 0x62, 0x31, + 0x95, 0x40, 0x62, 0x7b, 0x32, 0xc1, 0xb6, 0x62, 0xbc, 0x9e, 0xef, 0xef, 0x1b, 0xed, 0xd7, 0x46, + 0x58, 0x59, 0xdf, 0x84, 0x34, 0x0d, 0xf5, 0x1b, 0x19, 0x66, 0x2a, 0xb2, 0x0f, 0xc2, 0xd4, 0xc9, + 0x4c, 0xd7, 0x9b, 0xbb, 0xd4, 0xf5, 0x66, 0x98, 0xe4, 0xb0, 0xe0, 0x76, 0x99, 0x5d, 0x35, 0x81, + 0x25, 0x57, 0xa1, 0x60, 0xc8, 0xa7, 0x66, 0x67, 0xa2, 0x9d, 0xe0, 0xd7, 0xa3, 0x09, 0x2a, 0x72, + 0x19, 0xf2, 0x6c, 0xc9, 0x8a, 0x6f, 0x99, 0x65, 0xeb, 0x42, 0x43, 0x0a, 0xf5, 0x7b, 0xb3, 0x30, + 0x24, 0x7d, 0xcd, 0xf5, 0x7d, 0x7d, 0xce, 0x8b, 0x3b, 0x6b, 0xa6, 0xef, 0xd9, 0x82, 0x7b, 0x29, + 0xbf, 0xc9, 0x37, 0x02, 0x51, 0xec, 0xe8, 0xd6, 0x49, 0x08, 0xe6, 0x39, 0xf1, 0xa1, 0x85, 0x9d, + 0x6f, 0x1f, 0x19, 0xfd, 0xeb, 0xf9, 0xfe, 0xec, 0x68, 0xee, 0xf5, 0x7c, 0x7f, 0x7e, 0xb4, 0x17, + 0xc3, 0x65, 0x61, 0x84, 0x6a, 0xbe, 0x37, 0xb7, 0x96, 0xcd, 0x95, 0x23, 0xfe, 0xc4, 0xe3, 0x60, + 0x43, 0x89, 0xc5, 0x64, 0x73, 0xc4, 0xdf, 0x7b, 0xbc, 0xab, 0xb2, 0x39, 0x4e, 0x8a, 0x28, 0x64, + 0xf3, 0x67, 0x19, 0x50, 0x52, 0x65, 0x53, 0x3a, 0x24, 0x67, 0x87, 0x83, 0x4b, 0x8d, 0xf8, 0xad, + 0x2c, 0x8c, 0x55, 0x2c, 0x8f, 0xae, 0xf0, 0x1d, 0xe3, 0x11, 0x9f, 0x2a, 0x6e, 0xc1, 0xa0, 0xf4, + 0x31, 0xa2, 0xcf, 0x1f, 0x0e, 0xf6, 0xe3, 0x21, 0xaa, 0x03, 0x27, 0xb9, 0xf4, 0x01, 0x66, 0x53, + 0x8f, 0x09, 0xf9, 0x88, 0xcf, 0x39, 0x47, 0x43, 0xc8, 0x47, 0x7c, 0xf2, 0x7a, 0x8f, 0x0a, 0xf9, + 0x5f, 0x66, 0xe0, 0x64, 0x4a, 0xe5, 0xe4, 0x12, 0xf4, 0x55, 0xdb, 0x4b, 0x18, 0x1d, 0x2b, 0x13, + 0xba, 0x05, 0xbb, 0xed, 0x25, 0x0c, 0x8c, 0xa5, 0xf9, 0x48, 0xb2, 0x88, 0x6f, 0xe0, 0xe7, 0x2b, + 0xe5, 0x09, 0x21, 0x55, 0x55, 0x7a, 0xcd, 0xcf, 0xc0, 0x69, 0x5f, 0x16, 0xbc, 0x93, 0xb7, 0x4d, + 0xa3, 0x1e, 0x7b, 0x27, 0xcf, 0xca, 0x90, 0x8f, 0xc3, 0x40, 0xe9, 0xed, 0xb6, 0x43, 0x91, 0x2f, + 0x97, 0xf8, 0xfb, 0x02, 0xbe, 0x3e, 0x22, 0x8d, 0x33, 0x7f, 0xf2, 0xcf, 0x28, 0xe2, 0xbc, 0x43, + 0x86, 0xea, 0xe7, 0x33, 0x70, 0xae, 0x73, 0xeb, 0xc8, 0x07, 0xa1, 0x8f, 0xed, 0xcc, 0x4b, 0xda, + 0x9c, 0xf8, 0x74, 0x9e, 0x46, 0xd4, 0x6e, 0xd0, 0x9a, 0xee, 0xc8, 0xc6, 0xbe, 0x4f, 0x46, 0x5e, + 0x81, 0xc1, 0x8a, 0xeb, 0xb6, 0xa9, 0x53, 0x7d, 0xe6, 0xb6, 0x56, 0x11, 0x7b, 0x42, 0xdc, 0x73, + 0x98, 0x08, 0xae, 0xb9, 0xcf, 0xc4, 0xe2, 0x5f, 0xc9, 0xf4, 0xea, 0x67, 0x32, 0x70, 0xbe, 0xdb, + 0x57, 0x91, 0x67, 0xa0, 0x7f, 0x91, 0x5a, 0xba, 0xe5, 0x55, 0xca, 0xa2, 0x49, 0xb8, 0xc5, 0xf2, + 0x10, 0x16, 0xdd, 0x29, 0x04, 0x84, 0xac, 0x10, 0x3f, 0x57, 0x0c, 0x1c, 0x19, 0xf8, 0x19, 0x28, + 0xc2, 0x62, 0x85, 0x7c, 0x42, 0xf5, 0x17, 0x96, 0xa0, 0x77, 0xde, 0xa2, 0xf3, 0xcb, 0xe4, 0x69, + 0x18, 0x60, 0xba, 0x8f, 0x39, 0xfc, 0xc5, 0x40, 0x1b, 0x93, 0x07, 0x0c, 0x22, 0xa6, 0x7b, 0xb4, + 0x90, 0x8a, 0xdc, 0x90, 0x13, 0x87, 0x0b, 0x75, 0x20, 0x72, 0x19, 0x8e, 0x99, 0xee, 0xd1, 0xe4, + 0x04, 0xe3, 0x37, 0xe4, 0x84, 0xcd, 0xa2, 0xb3, 0x23, 0xa5, 0x38, 0xc6, 0x2f, 0x25, 0xa6, 0x81, + 0x99, 0xb4, 0xac, 0xc6, 0x71, 0x9b, 0x20, 0x49, 0x31, 0xdd, 0xa3, 0xa5, 0x67, 0x43, 0x1e, 0x92, + 0x7d, 0xa1, 0xe2, 0x57, 0x99, 0x32, 0x6e, 0xba, 0x47, 0x8b, 0xd0, 0x92, 0xe7, 0x61, 0x50, 0xfc, + 0x7e, 0xdd, 0x36, 0xad, 0x78, 0x20, 0x0c, 0x09, 0x35, 0xdd, 0xa3, 0xc9, 0x94, 0x52, 0xa5, 0x0b, + 0x8e, 0x69, 0x79, 0xe2, 0x79, 0x5d, 0xbc, 0x52, 0xc4, 0x49, 0x95, 0xe2, 0x6f, 0xf2, 0x0a, 0x0c, + 0x07, 0x11, 0x46, 0xde, 0xa2, 0x75, 0x4f, 0x1c, 0xe9, 0x9c, 0x8e, 0x15, 0xe6, 0xc8, 0xe9, 0x1e, + 0x2d, 0x4a, 0x4d, 0x2e, 0x43, 0x41, 0xa3, 0xae, 0xf9, 0xb6, 0x7f, 0x09, 0x32, 0x22, 0x4d, 0x67, + 0xe6, 0xdb, 0x4c, 0x4a, 0x02, 0xcf, 0x7a, 0x27, 0xbc, 0x75, 0x11, 0x07, 0x30, 0x24, 0x56, 0xcb, + 0xa4, 0x65, 0xb0, 0xde, 0x91, 0xae, 0xdc, 0x5e, 0x0b, 0xe3, 0xae, 0x88, 0x6c, 0x6d, 0x83, 0xf1, + 0x07, 0xae, 0x32, 0x76, 0xba, 0x47, 0x8b, 0xd1, 0x4b, 0x52, 0x2d, 0x9b, 0xee, 0x9a, 0x08, 0x75, + 0x17, 0x97, 0x2a, 0x43, 0x49, 0x52, 0x65, 0x3f, 0xa5, 0xaa, 0xe7, 0xa8, 0xb7, 0x6e, 0x3b, 0x6b, + 0x22, 0xb0, 0x5d, 0xbc, 0x6a, 0x81, 0x95, 0xaa, 0x16, 0x10, 0xb9, 0x6a, 0xb6, 0xc8, 0x8c, 0xa4, + 0x57, 0xad, 0x7b, 0xba, 0x5c, 0x35, 0xdf, 0x5f, 0xfa, 0x9d, 0x34, 0x43, 0xf5, 0x7b, 0x3c, 0x69, + 0x6e, 0xb2, 0x43, 0x11, 0x27, 0x75, 0x28, 0xfe, 0x66, 0x95, 0x4a, 0x89, 0x51, 0x45, 0x56, 0xdc, + 0xa0, 0x52, 0x09, 0xc5, 0x2a, 0x95, 0x53, 0xa8, 0xde, 0x90, 0xf3, 0x85, 0x2a, 0x63, 0xd1, 0x0e, + 0x0a, 0x31, 0xac, 0x83, 0xa4, 0xbc, 0xa2, 0x45, 0xcc, 0x45, 0xa8, 0x10, 0x24, 0x1f, 0x0c, 0x5a, + 0x38, 0xb1, 0x30, 0xdd, 0xa3, 0x61, 0x96, 0x42, 0x95, 0x67, 0xb9, 0x54, 0x4e, 0x22, 0xc5, 0x90, + 0x4f, 0xc1, 0x60, 0xd3, 0x3d, 0x1a, 0xcf, 0x80, 0xf9, 0xb4, 0x94, 0x4f, 0x4a, 0x39, 0x15, 0x9d, + 0x22, 0x02, 0x04, 0x9b, 0x22, 0xc2, 0xac, 0x53, 0x53, 0xc9, 0x9c, 0x4b, 0xca, 0xe9, 0xe8, 0x8a, + 0x1a, 0xc7, 0x4f, 0xf7, 0x68, 0xc9, 0x3c, 0x4d, 0xcf, 0x47, 0xd2, 0x10, 0x29, 0x67, 0x62, 0xd1, + 0x67, 0x42, 0x14, 0x13, 0x97, 0x9c, 0xb0, 0x68, 0x3e, 0x35, 0x71, 0xb8, 0x72, 0x36, 0xba, 0x1c, + 0xa7, 0x90, 0x4c, 0xf7, 0x68, 0xa9, 0x29, 0xc7, 0x27, 0x12, 0xc9, 0x80, 0x14, 0x25, 0x7a, 0xe3, + 0x1b, 0x43, 0x4f, 0xf7, 0x68, 0x89, 0xf4, 0x41, 0x37, 0xe4, 0x2c, 0x3c, 0xca, 0x43, 0xd1, 0x4e, + 0x0c, 0x31, 0xac, 0x13, 0xa5, 0x6c, 0x3d, 0x37, 0xe4, 0xcc, 0x2c, 0xca, 0xb9, 0x64, 0xa9, 0x70, + 0xe6, 0x94, 0x32, 0xb8, 0x68, 0xe9, 0xc9, 0x26, 0x94, 0x87, 0x45, 0xba, 0x3f, 0x51, 0x3e, 0x8d, + 0x66, 0xba, 0x47, 0x4b, 0x4f, 0x54, 0xa1, 0xa5, 0x67, 0x69, 0x50, 0xce, 0x77, 0xe3, 0x19, 0xb4, + 0x2e, 0x3d, 0xc3, 0x83, 0xde, 0x25, 0x66, 0xbe, 0x72, 0x21, 0x1a, 0xfa, 0xb2, 0x23, 0xe1, 0x74, + 0x8f, 0xd6, 0x25, 0xf2, 0xfe, 0xed, 0x0e, 0x01, 0xec, 0x95, 0x8b, 0xd1, 0x6c, 0x9f, 0xa9, 0x44, + 0xd3, 0x3d, 0x5a, 0x87, 0xf0, 0xf7, 0xb7, 0x3b, 0xc4, 0x37, 0x57, 0x8a, 0x5d, 0xd9, 0x06, 0xf2, + 0xe8, 0x10, 0x1d, 0x7d, 0x3e, 0x35, 0x34, 0xb8, 0xf2, 0x48, 0x54, 0x75, 0x53, 0x48, 0x98, 0xea, + 0xa6, 0x05, 0x15, 0x9f, 0x4f, 0x8d, 0x65, 0xad, 0x3c, 0xda, 0x85, 0x61, 0xd0, 0xc6, 0xd4, 0x28, + 0xd8, 0xf3, 0xa9, 0xc1, 0xa4, 0x15, 0x35, 0xca, 0x30, 0x85, 0x84, 0x31, 0x4c, 0x0b, 0x43, 0x3d, + 0x9f, 0x1a, 0x73, 0x58, 0x79, 0xac, 0x0b, 0xc3, 0xb0, 0x85, 0x69, 0xd1, 0x8a, 0x9f, 0x8f, 0x04, + 0xfd, 0x55, 0xde, 0x17, 0x9d, 0x37, 0x24, 0x14, 0x9b, 0x37, 0xe4, 0xf0, 0xc0, 0x13, 0x89, 0xb0, + 0x86, 0xca, 0xe3, 0xd1, 0x61, 0x1e, 0x43, 0xb3, 0x61, 0x1e, 0x0f, 0x84, 0x38, 0x91, 0x08, 0xef, + 0xa6, 0x5c, 0xea, 0xc4, 0x04, 0xd1, 0x51, 0x26, 0x3c, 0x20, 0x5c, 0x25, 0x25, 0xbe, 0x98, 0xf2, + 0xfe, 0xa8, 0xb7, 0x62, 0x82, 0x60, 0xba, 0x47, 0x4b, 0x89, 0x4a, 0xa6, 0xa5, 0x07, 0xd3, 0x50, + 0x2e, 0x47, 0x87, 0x6d, 0x1a, 0x0d, 0x1b, 0xb6, 0xa9, 0x81, 0x38, 0x66, 0xd2, 0x5c, 0xaa, 0x95, + 0x2b, 0x51, 0xc3, 0x2c, 0x49, 0xc1, 0x0c, 0xb3, 0x14, 0x57, 0x6c, 0x2d, 0x3d, 0x3c, 0x84, 0xf2, + 0x44, 0xd7, 0x16, 0x22, 0x4d, 0x4a, 0x0b, 0x79, 0xb4, 0x84, 0xd0, 0x76, 0xba, 0xdd, 0x6a, 0xd8, + 0xba, 0xa1, 0x7c, 0x20, 0xd5, 0x76, 0xe2, 0x48, 0xc9, 0x76, 0xe2, 0x00, 0xb6, 0xca, 0xcb, 0x9e, + 0xbb, 0xca, 0x93, 0xd1, 0x55, 0x5e, 0xc6, 0xb1, 0x55, 0x3e, 0xe2, 0xe5, 0x3b, 0x91, 0xf0, 0x72, + 0x55, 0x9e, 0x8a, 0x2a, 0x40, 0x0c, 0xcd, 0x14, 0x20, 0xee, 0x17, 0xfb, 0xc9, 0xce, 0x7e, 0xa1, + 0xca, 0x55, 0xe4, 0xf6, 0x88, 0xcf, 0xad, 0x13, 0xdd, 0x74, 0x8f, 0xd6, 0xd9, 0xb7, 0xb4, 0x92, + 0xe2, 0xe6, 0xa9, 0x5c, 0x8b, 0x2a, 0x58, 0x82, 0x80, 0x29, 0x58, 0xd2, 0x39, 0xb4, 0x92, 0xe2, + 0xa7, 0xa9, 0x7c, 0xb0, 0x23, 0xab, 0xe0, 0x9b, 0x53, 0xbc, 0x3b, 0x6f, 0xc8, 0x8e, 0x96, 0xca, + 0xd3, 0xd1, 0xc5, 0x2e, 0xc4, 0xb0, 0xc5, 0x4e, 0x72, 0xc8, 0xbc, 0x21, 0xbb, 0x18, 0x2a, 0xd7, + 0x93, 0xa5, 0xc2, 0x25, 0x52, 0x72, 0x45, 0xd4, 0xd2, 0x3d, 0xf3, 0x94, 0x67, 0xa2, 0x5a, 0x97, + 0x46, 0xc3, 0xb4, 0x2e, 0xd5, 0xab, 0x6f, 0x2a, 0xe9, 0x60, 0xa7, 0xdc, 0x88, 0x9f, 0x25, 0x44, + 0xf1, 0xcc, 0xf2, 0x49, 0x38, 0xe5, 0xbd, 0x16, 0x8f, 0xf4, 0xa4, 0x3c, 0x1b, 0xbb, 0xcc, 0x88, + 0x60, 0x99, 0x7d, 0x1b, 0x8b, 0x0c, 0xf5, 0x5a, 0x3c, 0x38, 0x92, 0xf2, 0x5c, 0x3a, 0x87, 0x40, + 0x57, 0xe2, 0xc1, 0x94, 0x5e, 0x8b, 0xc7, 0x13, 0x52, 0x9e, 0x4f, 0xe7, 0x10, 0x48, 0x37, 0x1e, + 0x7f, 0xe8, 0x69, 0x29, 0xc2, 0xb1, 0xf2, 0xa1, 0xa8, 0xe9, 0x18, 0x20, 0x98, 0xe9, 0x18, 0xc6, + 0x41, 0x7e, 0x5a, 0x8a, 0x0c, 0xac, 0xbc, 0x90, 0x28, 0x12, 0x34, 0x56, 0x8a, 0x1f, 0xfc, 0xb4, + 0x14, 0x51, 0x57, 0x79, 0x31, 0x51, 0x24, 0x68, 0x9d, 0x14, 0x77, 0xd7, 0xe8, 0xf6, 0xf4, 0x4a, + 0x79, 0x29, 0x7a, 0xc4, 0xd1, 0x99, 0x72, 0xba, 0x47, 0xeb, 0xf6, 0x84, 0xeb, 0x93, 0x9d, 0xdd, + 0x15, 0x95, 0x97, 0xa3, 0x43, 0xb8, 0x13, 0x1d, 0x1b, 0xc2, 0x1d, 0x5d, 0x1e, 0x5f, 0x89, 0x3d, + 0xc3, 0x56, 0x5e, 0x89, 0x4e, 0x71, 0x11, 0x24, 0x9b, 0xe2, 0xe2, 0x8f, 0xb6, 0x23, 0xef, 0x8b, + 0x95, 0x0f, 0x47, 0xa7, 0x38, 0x19, 0xc7, 0xa6, 0xb8, 0xc8, 0x5b, 0xe4, 0x89, 0xc4, 0xb3, 0x57, + 0xe5, 0xd5, 0xe8, 0x14, 0x17, 0x43, 0xb3, 0x29, 0x2e, 0xfe, 0x50, 0xf6, 0x95, 0xd8, 0xeb, 0x4f, + 0xe5, 0xb5, 0xf4, 0xf6, 0x23, 0x52, 0x6e, 0x3f, 0x7f, 0x2b, 0xaa, 0xa5, 0x3f, 0x63, 0x54, 0x4a, + 0xd1, 0xf1, 0x9b, 0x46, 0xc3, 0xc6, 0x6f, 0xea, 0x13, 0xc8, 0xf8, 0xc6, 0x41, 0x68, 0xd5, 0x78, + 0x97, 0x8d, 0x43, 0x68, 0x8a, 0xa4, 0x80, 0x23, 0x7b, 0x64, 0xbe, 0x11, 0x9a, 0xe8, 0xb0, 0x47, + 0xf6, 0xb7, 0x41, 0x31, 0x7a, 0x36, 0xbb, 0x26, 0xbc, 0xe7, 0x94, 0x72, 0x74, 0x76, 0x4d, 0x10, + 0xb0, 0xd9, 0x35, 0xe9, 0x73, 0x37, 0x05, 0xa3, 0x42, 0x8b, 0xb8, 0x53, 0xa0, 0x69, 0xad, 0x28, + 0x93, 0xb1, 0x57, 0x44, 0x31, 0x3c, 0x9b, 0x9d, 0xe2, 0x30, 0x5c, 0xaf, 0x39, 0x6c, 0xa2, 0x61, + 0xb6, 0x96, 0x6c, 0xdd, 0x31, 0xaa, 0xd4, 0x32, 0x94, 0xa9, 0xd8, 0x7a, 0x9d, 0x42, 0x83, 0xeb, + 0x75, 0x0a, 0x1c, 0xa3, 0x1b, 0xc5, 0xe0, 0x1a, 0xad, 0x53, 0xf3, 0x1e, 0x55, 0x6e, 0x22, 0xdb, + 0x62, 0x27, 0xb6, 0x82, 0x6c, 0xba, 0x47, 0xeb, 0xc4, 0x81, 0xd9, 0xea, 0xb3, 0x1b, 0xd5, 0x37, + 0x66, 0x82, 0x97, 0xb3, 0x0b, 0x0e, 0x6d, 0xe9, 0x0e, 0x55, 0xa6, 0xa3, 0xb6, 0x7a, 0x2a, 0x11, + 0xb3, 0xd5, 0x53, 0x11, 0x49, 0xb6, 0xfe, 0x58, 0xa8, 0x74, 0x63, 0x1b, 0x8e, 0x88, 0xf4, 0xd2, + 0x6c, 0x76, 0x8a, 0x22, 0x98, 0x80, 0x66, 0x6c, 0x6b, 0x05, 0x4f, 0x2a, 0x5e, 0x8f, 0xce, 0x4e, + 0x9d, 0x29, 0xd9, 0xec, 0xd4, 0x19, 0xcb, 0x54, 0x3d, 0x8a, 0xe5, 0x63, 0xf0, 0x56, 0x54, 0xd5, + 0x53, 0x48, 0x98, 0xaa, 0xa7, 0x80, 0x93, 0x0c, 0x35, 0xea, 0x52, 0x4f, 0x99, 0xe9, 0xc6, 0x10, + 0x49, 0x92, 0x0c, 0x11, 0x9c, 0x64, 0x38, 0x45, 0xbd, 0xfa, 0xaa, 0x32, 0xdb, 0x8d, 0x21, 0x92, + 0x24, 0x19, 0x22, 0x98, 0x6d, 0x36, 0xa3, 0xe0, 0xf1, 0x76, 0x63, 0xcd, 0xef, 0xb3, 0xb9, 0xe8, + 0x66, 0xb3, 0x23, 0x21, 0xdb, 0x6c, 0x76, 0x44, 0x92, 0xcf, 0xec, 0xd8, 0xbb, 0x53, 0x99, 0xc7, + 0x0a, 0xaf, 0x86, 0x76, 0xc1, 0x4e, 0x4a, 0x4d, 0xf7, 0x68, 0x3b, 0xf5, 0x1e, 0xfd, 0x40, 0xe0, + 0x0a, 0xa5, 0x2c, 0x60, 0x55, 0x27, 0x82, 0xb3, 0x0a, 0x0e, 0x9e, 0xee, 0xd1, 0x02, 0x67, 0xa9, + 0xe7, 0x61, 0x10, 0x3f, 0xaa, 0x62, 0x99, 0x5e, 0x79, 0x5c, 0x79, 0x23, 0xba, 0x65, 0x92, 0x50, + 0x6c, 0xcb, 0x24, 0xfd, 0x64, 0x93, 0x38, 0xfe, 0xe4, 0x53, 0x4c, 0x79, 0x5c, 0xd1, 0xa2, 0x93, + 0x78, 0x04, 0xc9, 0x26, 0xf1, 0x08, 0x20, 0xa8, 0xb7, 0xec, 0xd8, 0xad, 0xf2, 0xb8, 0x52, 0x4d, + 0xa9, 0x97, 0xa3, 0x82, 0x7a, 0xf9, 0xcf, 0xa0, 0xde, 0xea, 0x6a, 0xdb, 0x2b, 0xb3, 0x6f, 0x5c, + 0x4c, 0xa9, 0xd7, 0x47, 0x06, 0xf5, 0xfa, 0x00, 0x36, 0x15, 0x22, 0x60, 0xc1, 0xb1, 0xd9, 0xa4, + 0x7d, 0xcb, 0x6c, 0x34, 0x94, 0xdb, 0xd1, 0xa9, 0x30, 0x8e, 0x67, 0x53, 0x61, 0x1c, 0xc6, 0x4c, + 0x4f, 0xde, 0x2a, 0xba, 0xd4, 0x5e, 0x51, 0xee, 0x44, 0x4d, 0xcf, 0x10, 0xc3, 0x4c, 0xcf, 0xf0, + 0x17, 0xee, 0x2e, 0xd8, 0x2f, 0x8d, 0x2e, 0x3b, 0xd4, 0x5d, 0x55, 0xee, 0xc6, 0x76, 0x17, 0x12, + 0x0e, 0x77, 0x17, 0xd2, 0x6f, 0xb2, 0x02, 0x0f, 0x47, 0x16, 0x1a, 0xff, 0xee, 0xa9, 0x4a, 0x75, + 0xa7, 0xbe, 0xaa, 0x7c, 0x04, 0x59, 0x3d, 0x96, 0xba, 0x54, 0x45, 0x49, 0xa7, 0x7b, 0xb4, 0x6e, + 0x9c, 0x70, 0x5b, 0xfe, 0xc6, 0x0c, 0x0f, 0x43, 0xa8, 0x2d, 0x4c, 0xf8, 0x9b, 0xd0, 0x37, 0x63, + 0xdb, 0xf2, 0x24, 0x09, 0x6e, 0xcb, 0x93, 0x60, 0xd2, 0x82, 0x8b, 0xb1, 0xad, 0xda, 0xac, 0xde, + 0x60, 0xfb, 0x12, 0x6a, 0x2c, 0xe8, 0xf5, 0x35, 0xea, 0x29, 0x1f, 0x45, 0xde, 0x97, 0x3a, 0x6c, + 0xf8, 0x62, 0xd4, 0xd3, 0x3d, 0xda, 0x36, 0xfc, 0x88, 0x0a, 0xf9, 0xea, 0xd4, 0xe2, 0x82, 0xf2, + 0xb1, 0xe8, 0xf9, 0x26, 0x83, 0x4d, 0xf7, 0x68, 0x88, 0x63, 0x56, 0xda, 0xed, 0xd6, 0x8a, 0xa3, + 0x1b, 0x94, 0x1b, 0x5a, 0x68, 0xbb, 0x09, 0x03, 0xf4, 0xe3, 0x51, 0x2b, 0xad, 0x13, 0x1d, 0xb3, + 0xd2, 0x3a, 0xe1, 0x98, 0xa2, 0x46, 0x22, 0xee, 0x2b, 0x9f, 0x88, 0x2a, 0x6a, 0x04, 0xc9, 0x14, + 0x35, 0x1a, 0x9f, 0xff, 0x23, 0x70, 0x26, 0xd8, 0xcf, 0x8b, 0xf5, 0x97, 0x77, 0x9a, 0xf2, 0x49, + 0xe4, 0x73, 0x31, 0x71, 0x19, 0x10, 0xa1, 0x9a, 0xee, 0xd1, 0x3a, 0x94, 0x67, 0x2b, 0x6e, 0x22, + 0x99, 0x8c, 0x30, 0x2f, 0xbe, 0x27, 0xba, 0xe2, 0x76, 0x20, 0x63, 0x2b, 0x6e, 0x07, 0x54, 0x2a, + 0x73, 0x21, 0x54, 0x7d, 0x1b, 0xe6, 0x81, 0x4c, 0x3b, 0x71, 0x48, 0x65, 0x2e, 0x2c, 0xb5, 0xa5, + 0x6d, 0x98, 0x07, 0xd6, 0x5a, 0x27, 0x0e, 0xe4, 0x32, 0x14, 0xaa, 0xd5, 0x59, 0xad, 0x6d, 0x29, + 0xf5, 0x98, 0x0f, 0x18, 0x42, 0xa7, 0x7b, 0x34, 0x81, 0x67, 0x66, 0xd0, 0x64, 0x43, 0x77, 0x3d, + 0xb3, 0xee, 0xe2, 0x88, 0xf1, 0x47, 0x88, 0x11, 0x35, 0x83, 0xd2, 0x68, 0x98, 0x19, 0x94, 0x06, + 0x67, 0xf6, 0xe2, 0x84, 0xee, 0xba, 0xba, 0x65, 0x38, 0xfa, 0x38, 0x2e, 0x13, 0x34, 0xf6, 0xc6, + 0x20, 0x82, 0x65, 0xf6, 0x62, 0x14, 0x82, 0x87, 0xef, 0x3e, 0xc4, 0x37, 0x73, 0x96, 0x63, 0x87, + 0xef, 0x31, 0x3c, 0x1e, 0xbe, 0xc7, 0x60, 0x68, 0x77, 0xfa, 0x30, 0x8d, 0xae, 0x98, 0x4c, 0x44, + 0xca, 0x4a, 0xcc, 0xee, 0x8c, 0x13, 0xa0, 0xdd, 0x19, 0x07, 0x46, 0x9a, 0xe4, 0x2f, 0xb7, 0xab, + 0x1d, 0x9a, 0x14, 0xae, 0xb2, 0x89, 0x32, 0x6c, 0xfd, 0x0e, 0x07, 0x47, 0x79, 0xc3, 0xd2, 0x9b, + 0x76, 0x79, 0xdc, 0x97, 0xba, 0x19, 0x5d, 0xbf, 0x3b, 0x12, 0xb2, 0xf5, 0xbb, 0x23, 0x92, 0xcd, + 0xae, 0xfe, 0x46, 0x6b, 0x55, 0x77, 0xa8, 0x51, 0x36, 0x1d, 0x3c, 0x59, 0xdc, 0xe0, 0x5b, 0xc3, + 0xb7, 0xa2, 0xb3, 0x6b, 0x17, 0x52, 0x36, 0xbb, 0x76, 0x41, 0x33, 0x23, 0x2f, 0x1d, 0xad, 0x51, + 0xdd, 0x50, 0xd6, 0xa2, 0x46, 0x5e, 0x67, 0x4a, 0x66, 0xe4, 0x75, 0xc6, 0x76, 0xfe, 0x9c, 0xbb, + 0x8e, 0xe9, 0x51, 0xa5, 0xb1, 0x93, 0xcf, 0x41, 0xd2, 0xce, 0x9f, 0x83, 0x68, 0xb6, 0x21, 0x8c, + 0x77, 0x48, 0x33, 0xba, 0x21, 0x4c, 0x76, 0x43, 0xbc, 0x04, 0xb3, 0x58, 0xc4, 0x53, 0x13, 0xc5, + 0x8a, 0x5a, 0x2c, 0x02, 0xcc, 0x2c, 0x96, 0xf0, 0x31, 0x4a, 0xe4, 0x81, 0x81, 0x62, 0x47, 0xd7, + 0x50, 0x19, 0xc7, 0xd6, 0xd0, 0xc8, 0x63, 0x84, 0xe7, 0x23, 0xde, 0xb3, 0x4a, 0x2b, 0x6a, 0x75, + 0x48, 0x28, 0x66, 0x75, 0xc8, 0x7e, 0xb6, 0x13, 0x70, 0x02, 0x6f, 0xc1, 0xb5, 0x76, 0x70, 0x8f, + 0xf3, 0xa9, 0xe8, 0x67, 0xc6, 0xd0, 0xec, 0x33, 0x63, 0xa0, 0x08, 0x13, 0x31, 0x6d, 0x39, 0x1d, + 0x98, 0x84, 0xe7, 0x83, 0x31, 0x10, 0x99, 0x01, 0x52, 0x2d, 0xcd, 0xce, 0x54, 0x8c, 0x05, 0xf9, + 0x8a, 0xcc, 0x8d, 0x9e, 0xc0, 0x26, 0x29, 0xa6, 0x7b, 0xb4, 0x94, 0x72, 0xe4, 0x2d, 0x38, 0x2f, + 0xa0, 0xe2, 0x1d, 0x21, 0xe6, 0x9c, 0x36, 0x82, 0x05, 0xc1, 0x8b, 0x7a, 0x67, 0x74, 0xa3, 0x9d, + 0xee, 0xd1, 0xba, 0xf2, 0xea, 0x5c, 0x97, 0x58, 0x1f, 0xda, 0x3b, 0xa9, 0x2b, 0x58, 0x24, 0xba, + 0xf2, 0xea, 0x5c, 0x97, 0x90, 0xfb, 0xbd, 0x9d, 0xd4, 0x15, 0x74, 0x42, 0x57, 0x5e, 0xc4, 0x85, + 0x62, 0x37, 0x7c, 0xa9, 0xd1, 0x50, 0xd6, 0xb1, 0xba, 0xf7, 0xef, 0xa4, 0xba, 0x12, 0x1a, 0x9c, + 0xdb, 0x71, 0x64, 0xb3, 0xf4, 0x7c, 0x8b, 0x5a, 0xd5, 0xc8, 0x02, 0x74, 0x3f, 0x3a, 0x4b, 0x27, + 0x08, 0xd8, 0x2c, 0x9d, 0x00, 0xb2, 0x01, 0x25, 0x3b, 0x61, 0x2b, 0x1b, 0xd1, 0x01, 0x25, 0xe3, + 0xd8, 0x80, 0x8a, 0x38, 0x6c, 0xcf, 0xc3, 0xc9, 0xf9, 0x35, 0x4f, 0xf7, 0x2d, 0x48, 0x57, 0x74, + 0xe5, 0xdb, 0xb1, 0x4b, 0xa6, 0x24, 0x09, 0x5e, 0x32, 0x25, 0xc1, 0x6c, 0x8c, 0x30, 0x70, 0x75, + 0xc3, 0xaa, 0x4f, 0xe9, 0x66, 0xa3, 0xed, 0x50, 0xe5, 0x3f, 0x89, 0x8e, 0x91, 0x18, 0x9a, 0x8d, + 0x91, 0x18, 0x88, 0x2d, 0xd0, 0x0c, 0x54, 0x72, 0x5d, 0x73, 0xc5, 0x12, 0xfb, 0xca, 0x76, 0xc3, + 0x53, 0xfe, 0xd3, 0xe8, 0x02, 0x9d, 0x46, 0xc3, 0x16, 0xe8, 0x34, 0x38, 0x9e, 0x3a, 0xa5, 0xe4, + 0x63, 0x57, 0xfe, 0xb3, 0xd8, 0xa9, 0x53, 0x0a, 0x0d, 0x9e, 0x3a, 0xa5, 0xe5, 0x72, 0x9f, 0x82, + 0x51, 0x6e, 0x93, 0xcd, 0x98, 0xc1, 0x5d, 0xf5, 0x7f, 0x1e, 0x5d, 0x1f, 0xe3, 0x78, 0xb6, 0x3e, + 0xc6, 0x61, 0x51, 0x3e, 0xa2, 0x0b, 0xfe, 0x8b, 0x4e, 0x7c, 0x02, 0xf9, 0x27, 0xca, 0x90, 0x9b, + 0x32, 0x1f, 0x31, 0x52, 0xbe, 0x37, 0xd3, 0x89, 0x51, 0x30, 0x3c, 0x12, 0x85, 0xa2, 0x8c, 0x34, + 0x7a, 0xcf, 0xa4, 0xeb, 0xca, 0xa7, 0x3b, 0x32, 0xe2, 0x04, 0x51, 0x46, 0x1c, 0x46, 0xde, 0x84, + 0x33, 0x21, 0x6c, 0x96, 0x36, 0x97, 0x82, 0x99, 0xe9, 0xfb, 0x32, 0x51, 0x33, 0x38, 0x9d, 0x8c, + 0x99, 0xc1, 0xe9, 0x98, 0x34, 0xd6, 0x42, 0x74, 0xff, 0xe5, 0x36, 0xac, 0x03, 0x09, 0x76, 0x60, + 0x90, 0xc6, 0x5a, 0x48, 0xf3, 0xfb, 0xb7, 0x61, 0x1d, 0xc8, 0xb4, 0x03, 0x03, 0xf2, 0xd9, 0x0c, + 0x5c, 0x4a, 0x47, 0x95, 0x1a, 0x8d, 0x29, 0xdb, 0x09, 0x71, 0xca, 0x0f, 0x64, 0xa2, 0x07, 0x0d, + 0x3b, 0x2b, 0x36, 0xdd, 0xa3, 0xed, 0xb0, 0x02, 0xf2, 0x61, 0x18, 0x2e, 0xb5, 0x0d, 0xd3, 0xc3, + 0x8b, 0x37, 0x66, 0x38, 0xff, 0x60, 0x26, 0xb6, 0xc5, 0x91, 0xb1, 0xb8, 0xc5, 0x91, 0x01, 0xe4, + 0x75, 0x18, 0xab, 0xd2, 0x7a, 0xdb, 0x31, 0xbd, 0x0d, 0x0d, 0x73, 0xed, 0x33, 0x1e, 0x3f, 0x94, + 0x89, 0x4e, 0x62, 0x09, 0x0a, 0x36, 0x89, 0x25, 0x80, 0xe4, 0x4e, 0x87, 0x8c, 0xec, 0xca, 0x67, + 0x32, 0x5d, 0xaf, 0xe5, 0x83, 0xbe, 0xec, 0x90, 0xd0, 0x7d, 0x21, 0x35, 0xc3, 0xb5, 0xf2, 0xd9, + 0x4c, 0x97, 0x6b, 0x74, 0x69, 0x86, 0x4b, 0x49, 0x8e, 0xbd, 0x90, 0x9a, 0x7e, 0x58, 0xf9, 0xe1, + 0x4c, 0x97, 0x6b, 0xef, 0x90, 0x63, 0x5a, 0xe6, 0xe2, 0x67, 0xb9, 0xa7, 0x88, 0x60, 0xf4, 0x5f, + 0x65, 0x92, 0xae, 0x22, 0x41, 0x79, 0x89, 0x90, 0x15, 0xbb, 0xed, 0x06, 0x4a, 0xff, 0xb9, 0x4c, + 0xd2, 0x37, 0x2f, 0x2c, 0x16, 0xfe, 0x22, 0x14, 0xce, 0x4d, 0xde, 0xf7, 0xa8, 0x63, 0xe9, 0x0d, + 0xec, 0xce, 0xaa, 0x67, 0x3b, 0xfa, 0x0a, 0x9d, 0xb4, 0xf4, 0xa5, 0x06, 0x55, 0x3e, 0x9f, 0x89, + 0x5a, 0xb0, 0x9d, 0x49, 0x99, 0x05, 0xdb, 0x19, 0x4b, 0x56, 0xe1, 0xe1, 0x34, 0x6c, 0xd9, 0x74, + 0xb1, 0x9e, 0x2f, 0x64, 0xa2, 0x26, 0x6c, 0x17, 0x5a, 0x66, 0xc2, 0x76, 0x41, 0x93, 0xeb, 0x30, + 0x30, 0x6e, 0xfb, 0xd3, 0xef, 0x8f, 0xc4, 0x9c, 0x21, 0x03, 0xcc, 0x74, 0x8f, 0x16, 0x92, 0x89, + 0x32, 0x62, 0x50, 0x7f, 0x31, 0x59, 0x26, 0xbc, 0x7c, 0x0a, 0x7e, 0x88, 0x32, 0x42, 0xdc, 0xff, + 0x75, 0xb2, 0x4c, 0x78, 0xc7, 0x15, 0xfc, 0x60, 0x33, 0x09, 0xaf, 0x71, 0x76, 0xaa, 0xc4, 0xec, + 0xb6, 0x89, 0x55, 0xbd, 0xd1, 0xa0, 0xd6, 0x0a, 0x55, 0xbe, 0x14, 0x9b, 0x49, 0xd2, 0xc9, 0xd8, + 0x4c, 0x92, 0x8e, 0x21, 0x1f, 0x87, 0xb3, 0x77, 0xf4, 0x86, 0x69, 0x84, 0x38, 0x3f, 0x19, 0xad, + 0xf2, 0xa3, 0x99, 0xe8, 0x6e, 0xba, 0x03, 0x1d, 0xdb, 0x4d, 0x77, 0x40, 0x91, 0x59, 0x20, 0xb8, + 0x8c, 0x06, 0xb3, 0x05, 0x5b, 0x9f, 0x95, 0xff, 0x26, 0x13, 0xb5, 0x53, 0x93, 0x24, 0xcc, 0x4e, + 0x4d, 0x42, 0x49, 0xad, 0x73, 0x54, 0x7b, 0xe5, 0xc7, 0x32, 0xd1, 0xd3, 0x9a, 0x4e, 0x84, 0xd3, + 0x3d, 0x5a, 0xe7, 0xd0, 0xf8, 0x37, 0x61, 0xb4, 0xba, 0x50, 0x99, 0x9a, 0x9a, 0xac, 0xde, 0xa9, + 0x94, 0xd1, 0x7d, 0xd7, 0x50, 0x7e, 0x3c, 0xb6, 0x62, 0xc5, 0x09, 0xd8, 0x8a, 0x15, 0x87, 0x91, + 0x97, 0x60, 0x88, 0xb5, 0x9f, 0x0d, 0x18, 0xfc, 0xe4, 0x2f, 0x67, 0xa2, 0xe6, 0x94, 0x8c, 0x64, + 0xe6, 0x94, 0xfc, 0x9b, 0x54, 0xe1, 0x14, 0x93, 0xe2, 0x82, 0x43, 0x97, 0xa9, 0x43, 0xad, 0xba, + 0x3f, 0xa6, 0x7f, 0x22, 0x13, 0xb5, 0x32, 0xd2, 0x88, 0x98, 0x95, 0x91, 0x06, 0x27, 0x6b, 0x70, + 0x3e, 0x7e, 0x12, 0x24, 0x3f, 0xa6, 0x52, 0x7e, 0x32, 0x13, 0x33, 0x86, 0xbb, 0x10, 0xa3, 0x31, + 0xdc, 0x05, 0x4f, 0x2c, 0xb8, 0x20, 0x8e, 0x55, 0x84, 0xc3, 0x65, 0xbc, 0xb6, 0xff, 0x96, 0xd7, + 0xf6, 0x78, 0xe8, 0x10, 0xd8, 0x85, 0x7a, 0xba, 0x47, 0xeb, 0xce, 0x8e, 0xe9, 0x59, 0x32, 0x76, + 0xbb, 0xf2, 0x53, 0x99, 0x74, 0x8f, 0x94, 0x88, 0x9b, 0x72, 0x5a, 0xd0, 0xf7, 0x37, 0x3b, 0x45, + 0x1e, 0x57, 0x7e, 0x3a, 0x36, 0xde, 0xd2, 0xc9, 0xd8, 0x78, 0xeb, 0x10, 0xba, 0xfc, 0x75, 0x18, + 0xe3, 0x4a, 0xbd, 0xa0, 0xe3, 0x30, 0xb4, 0x56, 0xa8, 0xa1, 0xfc, 0x77, 0xb1, 0xd5, 0x2e, 0x41, + 0x81, 0xae, 0x3d, 0x71, 0x20, 0x9b, 0xba, 0xab, 0x2d, 0xdd, 0xb2, 0xf0, 0x98, 0x55, 0xf9, 0xef, + 0x63, 0x53, 0x77, 0x88, 0x42, 0xc7, 0xdd, 0xe0, 0x17, 0xd3, 0x84, 0x6e, 0x59, 0x3b, 0x94, 0x9f, + 0x89, 0x69, 0x42, 0x37, 0x62, 0xa6, 0x09, 0x5d, 0x53, 0x80, 0xdc, 0xe9, 0xf0, 0xb0, 0x51, 0xf9, + 0x4a, 0x6c, 0x45, 0x4e, 0xa5, 0x62, 0x2b, 0x72, 0xfa, 0xbb, 0xc8, 0x3b, 0x1d, 0x1e, 0x05, 0x2a, + 0x3f, 0xdb, 0x9d, 0x6f, 0xb8, 0xd2, 0xa7, 0xbf, 0x29, 0xbc, 0xd3, 0xe1, 0x41, 0x9d, 0xf2, 0x73, + 0xdd, 0xf9, 0x86, 0x8e, 0x7d, 0xe9, 0xef, 0xf1, 0x6a, 0x9d, 0x1f, 0xa3, 0x29, 0x3f, 0x1f, 0x9f, + 0xba, 0x3a, 0x10, 0xe2, 0xd4, 0xd5, 0xe9, 0x45, 0xdb, 0x12, 0x3c, 0xc4, 0x35, 0xe4, 0xa6, 0xa3, + 0xb7, 0x56, 0xab, 0xd4, 0xf3, 0x4c, 0x6b, 0xc5, 0xdf, 0x89, 0xfd, 0x42, 0x26, 0x76, 0x3c, 0xd6, + 0x89, 0x12, 0x8f, 0xc7, 0x3a, 0x21, 0x99, 0xf2, 0x26, 0x9e, 0x9d, 0x29, 0xbf, 0x18, 0x53, 0xde, + 0x04, 0x05, 0x53, 0xde, 0xe4, 0x6b, 0xb5, 0xd7, 0x53, 0x5e, 0x57, 0x29, 0xff, 0x43, 0x67, 0x5e, + 0x41, 0xfb, 0x52, 0x1e, 0x65, 0xbd, 0x9e, 0xf2, 0x88, 0x48, 0xf9, 0x1f, 0x3b, 0xf3, 0x0a, 0x7d, + 0x90, 0x12, 0xc0, 0xf1, 0x3e, 0xe8, 0xc5, 0x5d, 0xad, 0xfa, 0x95, 0x0c, 0x0c, 0x55, 0x3d, 0x87, + 0xea, 0x4d, 0x11, 0x51, 0xe2, 0x1c, 0xf4, 0x73, 0xf7, 0x30, 0xff, 0x85, 0x86, 0x16, 0xfc, 0x26, + 0x97, 0x60, 0x64, 0x46, 0x77, 0x3d, 0x2c, 0x59, 0xb1, 0x0c, 0x7a, 0x1f, 0x9f, 0x46, 0xe4, 0xb4, + 0x18, 0x94, 0xcc, 0x70, 0x3a, 0x5e, 0x0e, 0x83, 0x08, 0xe5, 0xb6, 0x0d, 0xa4, 0xd0, 0xff, 0xce, + 0x66, 0xb1, 0x07, 0xe3, 0x26, 0xc4, 0xca, 0xaa, 0xdf, 0xc8, 0x40, 0xc2, 0x71, 0x6d, 0xef, 0x2f, + 0xa7, 0xe6, 0xe1, 0x44, 0x2c, 0x70, 0x95, 0x78, 0xdf, 0xb1, 0xc3, 0xb8, 0x56, 0xf1, 0xd2, 0xe4, + 0xfd, 0xc1, 0xbb, 0x82, 0xdb, 0xda, 0x8c, 0x08, 0x92, 0xd1, 0xb7, 0xb5, 0x59, 0xcc, 0xb5, 0x9d, + 0x86, 0x26, 0xa1, 0xc4, 0x23, 0xe8, 0xef, 0x8c, 0x86, 0x51, 0x79, 0xc8, 0x25, 0xf1, 0x8c, 0x2b, + 0x13, 0x86, 0xd6, 0x88, 0xe5, 0x7c, 0xe4, 0xcf, 0xb6, 0x3e, 0x0c, 0x43, 0x95, 0x66, 0x8b, 0x3a, + 0xae, 0x6d, 0xe9, 0x9e, 0xed, 0xe7, 0x96, 0xc7, 0xb0, 0x0b, 0xa6, 0x04, 0x97, 0x43, 0x01, 0xc8, + 0xf4, 0xe4, 0x8a, 0x9f, 0x86, 0x22, 0x87, 0xf1, 0x90, 0x30, 0xa8, 0x67, 0x3c, 0x8f, 0x20, 0xa7, + 0x60, 0xa4, 0xb7, 0x5d, 0x1d, 0x5f, 0xa0, 0x04, 0xa4, 0x6d, 0x06, 0x90, 0x49, 0x91, 0x82, 0x3c, + 0x09, 0x05, 0x3c, 0xb1, 0x73, 0x31, 0xbd, 0x8c, 0x08, 0xf8, 0xd1, 0x40, 0x88, 0x1c, 0x5e, 0x81, + 0xd3, 0x90, 0x5b, 0x30, 0x1a, 0x5e, 0x47, 0xdc, 0x74, 0xec, 0x76, 0xcb, 0x0f, 0x28, 0x8d, 0xf9, + 0x17, 0xd7, 0x02, 0x5c, 0x6d, 0x05, 0x91, 0x12, 0x8b, 0x44, 0x41, 0x32, 0x0d, 0x27, 0x42, 0x18, + 0x13, 0x91, 0x1f, 0xc8, 0x1e, 0x93, 0x08, 0x49, 0xbc, 0x98, 0x38, 0x23, 0x49, 0x84, 0x62, 0xc5, + 0x48, 0x05, 0xfa, 0xfc, 0x68, 0x1f, 0xfd, 0xdb, 0x2a, 0xe9, 0x49, 0x11, 0xed, 0xa3, 0x4f, 0x8e, + 0xf3, 0xe1, 0x97, 0x27, 0x53, 0x30, 0xa2, 0xd9, 0x6d, 0x8f, 0x2e, 0xda, 0x62, 0x1d, 0x17, 0x51, + 0x65, 0xb0, 0x4d, 0x0e, 0xc3, 0xd4, 0x3c, 0xdb, 0x4f, 0x5f, 0x29, 0xa7, 0x51, 0x8c, 0x96, 0x22, + 0x73, 0x30, 0x96, 0xb8, 0xb8, 0x91, 0x93, 0x4a, 0x4a, 0x9f, 0x97, 0x64, 0x96, 0x2c, 0x4a, 0x7e, + 0x30, 0x03, 0x85, 0x45, 0x47, 0x37, 0x3d, 0x57, 0x3c, 0x5e, 0x39, 0x7d, 0x75, 0xdd, 0xd1, 0x5b, + 0x4c, 0x3f, 0xae, 0x62, 0xc0, 0xab, 0x3b, 0x7a, 0xa3, 0x4d, 0xdd, 0xf1, 0xbb, 0xec, 0xeb, 0xfe, + 0xc1, 0x66, 0xf1, 0xa5, 0x15, 0xdc, 0x1e, 0x5e, 0xad, 0xdb, 0xcd, 0x6b, 0x2b, 0x8e, 0x7e, 0xcf, + 0xf4, 0x70, 0xea, 0xd0, 0x1b, 0xd7, 0x3c, 0xda, 0xc0, 0x5d, 0xe8, 0x35, 0xbd, 0x65, 0x5e, 0xc3, + 0xc0, 0x8a, 0xd7, 0x02, 0x4e, 0xbc, 0x06, 0xa6, 0x02, 0x1e, 0xfe, 0x25, 0xab, 0x00, 0xc7, 0x91, + 0x39, 0xb6, 0x79, 0xc3, 0x4f, 0x2d, 0xb5, 0x5a, 0xe2, 0x25, 0x8c, 0xb4, 0x77, 0xf3, 0x31, 0x5c, + 0xb1, 0x03, 0x81, 0xe9, 0xad, 0x96, 0x9c, 0xb6, 0x36, 0xa4, 0x63, 0x5a, 0xb0, 0x28, 0x5a, 0xe4, + 0x8b, 0x69, 0x38, 0x94, 0xb8, 0xdf, 0xd8, 0x14, 0x21, 0xc5, 0x8b, 0x91, 0x25, 0x38, 0x21, 0xf8, + 0x06, 0xa1, 0x87, 0x47, 0xa2, 0xb3, 0x42, 0x0c, 0xcd, 0x95, 0x36, 0x68, 0xa3, 0x21, 0xc0, 0x72, + 0x1d, 0xb1, 0x12, 0x64, 0x3c, 0xcc, 0x87, 0x36, 0xa7, 0x37, 0xa9, 0xab, 0x9c, 0x40, 0x8d, 0x3d, + 0xbf, 0xb5, 0x59, 0x54, 0xfc, 0xf2, 0x18, 0xf8, 0x26, 0x35, 0xbb, 0x27, 0x16, 0x91, 0x79, 0x70, + 0xad, 0x1f, 0x4d, 0xe1, 0x11, 0xd7, 0xf9, 0x68, 0x11, 0x32, 0x01, 0xc3, 0x81, 0x23, 0xee, 0xed, + 0xdb, 0x95, 0x32, 0x3e, 0xb5, 0x11, 0xb1, 0x8f, 0x62, 0xc1, 0x81, 0x65, 0x26, 0x91, 0x32, 0xd2, + 0x9b, 0x3c, 0xfe, 0xf6, 0x26, 0xf6, 0x26, 0xaf, 0x95, 0xf2, 0x26, 0x6f, 0x81, 0xbc, 0x02, 0x83, + 0xa5, 0xbb, 0x55, 0xf1, 0xd6, 0xd0, 0x55, 0x4e, 0x86, 0xe1, 0xe4, 0x31, 0xc1, 0xab, 0x78, 0x97, + 0x28, 0x37, 0x5d, 0xa6, 0x27, 0x93, 0x30, 0x12, 0xb9, 0xcb, 0x77, 0x95, 0x53, 0xc8, 0x01, 0x5b, + 0xae, 0x23, 0xa6, 0xe6, 0x08, 0x54, 0x24, 0xe5, 0x70, 0xa4, 0x10, 0xd3, 0x1a, 0xb6, 0x1d, 0x6e, + 0x34, 0xec, 0x75, 0x8d, 0xe2, 0xb3, 0x46, 0x7c, 0xb8, 0xd3, 0xcf, 0xb5, 0xc6, 0x10, 0xa8, 0x9a, + 0xc3, 0x71, 0x91, 0x1c, 0xc3, 0xd1, 0x62, 0xe4, 0x2d, 0x20, 0x18, 0xcc, 0x9b, 0x1a, 0xfe, 0xd1, + 0x6e, 0xa5, 0xec, 0x2a, 0x67, 0x30, 0xf0, 0x1f, 0x89, 0xbf, 0xab, 0xad, 0x94, 0xc7, 0x2f, 0x89, + 0xe9, 0xe3, 0xa2, 0xce, 0x4b, 0xd5, 0x1c, 0x81, 0xab, 0x99, 0x91, 0x4c, 0x67, 0x29, 0x5c, 0xc9, + 0x3a, 0x9c, 0x5d, 0x70, 0xe8, 0x3d, 0xd3, 0x6e, 0xbb, 0xfe, 0xf2, 0xe1, 0xcf, 0x5b, 0x67, 0xb7, + 0x9d, 0xb7, 0x1e, 0x15, 0x15, 0x9f, 0x6e, 0x39, 0xf4, 0x5e, 0xcd, 0x0f, 0xf7, 0x16, 0x89, 0x56, + 0xd4, 0x89, 0x3b, 0xe6, 0x6b, 0x7b, 0xbb, 0xed, 0x50, 0x01, 0x37, 0xa9, 0xab, 0x28, 0xe1, 0x54, + 0xcb, 0x5f, 0xa8, 0x9a, 0x01, 0x2e, 0x92, 0xaf, 0x2d, 0x5a, 0x8c, 0x68, 0x40, 0x6e, 0x4e, 0xf8, + 0xc7, 0xfc, 0xa5, 0x3a, 0xcf, 0x6a, 0xa5, 0x3c, 0x84, 0xcc, 0x54, 0x26, 0x96, 0x95, 0x7a, 0x10, + 0xfa, 0xb1, 0xa6, 0x0b, 0xbc, 0x2c, 0x96, 0x64, 0x69, 0x32, 0x03, 0xa3, 0x0b, 0x0e, 0x6e, 0x3a, + 0x6e, 0xd1, 0x8d, 0x05, 0xbb, 0x61, 0xd6, 0x37, 0xf0, 0xfd, 0x90, 0x98, 0x2a, 0x5b, 0x1c, 0x57, + 0x5b, 0xa3, 0x1b, 0xb5, 0x16, 0x62, 0xe5, 0x65, 0x25, 0x5e, 0x52, 0x0e, 0xc5, 0xf6, 0xf0, 0xce, + 0x42, 0xb1, 0x51, 0x18, 0x15, 0x97, 0x04, 0xf7, 0x3d, 0x6a, 0xb1, 0xa5, 0xde, 0x15, 0x6f, 0x85, + 0x94, 0xd8, 0xa5, 0x42, 0x80, 0x17, 0xf9, 0x86, 0xf9, 0x28, 0xa3, 0x01, 0x58, 0x6e, 0x58, 0xbc, + 0x48, 0x32, 0x5e, 0xd9, 0x85, 0x3d, 0xc4, 0x2b, 0xfb, 0x42, 0x4e, 0x9e, 0x7f, 0xc9, 0x79, 0xc8, + 0x4b, 0xe1, 0xc4, 0x31, 0x18, 0x13, 0x86, 0x5e, 0xcc, 0x8b, 0x18, 0x73, 0x03, 0xc2, 0x76, 0x09, + 0x5e, 0xdd, 0x62, 0x92, 0x98, 0x30, 0x40, 0x8f, 0x16, 0x12, 0x60, 0x82, 0x8e, 0xf6, 0x52, 0xc3, + 0xac, 0x63, 0x40, 0xce, 0x9c, 0x94, 0xa0, 0x03, 0xa1, 0x3c, 0x1e, 0xa7, 0x44, 0x42, 0xae, 0xc3, + 0xa0, 0xbf, 0xd9, 0x0d, 0x83, 0x91, 0x61, 0x9c, 0x46, 0x3f, 0xbd, 0x33, 0x0f, 0x03, 0x29, 0x11, + 0x91, 0x17, 0x31, 0xc1, 0xb9, 0xff, 0xa2, 0xb9, 0x37, 0xb4, 0x81, 0xe4, 0xd9, 0x23, 0x96, 0xe1, + 0xdc, 0x7f, 0xd8, 0x3c, 0x0e, 0xc3, 0xb2, 0x3a, 0xfa, 0x29, 0x89, 0x70, 0xe2, 0x8c, 0xe8, 0xb0, + 0xac, 0x20, 0xd1, 0x22, 0x64, 0x1e, 0xc6, 0x12, 0x1a, 0x28, 0x42, 0x97, 0x61, 0x5a, 0xca, 0x14, + 0xf5, 0x95, 0x17, 0xe6, 0x44, 0x59, 0xf5, 0xfb, 0xb2, 0x89, 0x65, 0x87, 0x09, 0x46, 0x50, 0x49, + 0x9d, 0x83, 0x82, 0xf1, 0x59, 0x73, 0xc1, 0x48, 0x44, 0xe4, 0x32, 0xf4, 0xc7, 0x72, 0x9c, 0xe3, + 0x1b, 0xf7, 0x20, 0xc1, 0x79, 0x80, 0x25, 0xd7, 0xa5, 0x24, 0x59, 0x52, 0xb0, 0x41, 0x3f, 0x49, + 0x56, 0x3c, 0xea, 0x1e, 0xa6, 0xcb, 0xba, 0x1e, 0x8b, 0xc7, 0xef, 0xa7, 0xa2, 0x4e, 0x2e, 0x79, + 0x61, 0xfc, 0xfd, 0xc0, 0xe0, 0xec, 0xdd, 0xce, 0xe0, 0x54, 0x7f, 0x2f, 0x93, 0x1c, 0x42, 0xe4, + 0x46, 0x32, 0xee, 0x17, 0xcf, 0x42, 0xed, 0x03, 0xe5, 0x5a, 0x83, 0x08, 0x60, 0x91, 0x08, 0x5e, + 0xd9, 0x3d, 0x47, 0xf0, 0xca, 0xed, 0x32, 0x82, 0x97, 0xfa, 0xef, 0xf2, 0x5d, 0xfd, 0xd5, 0x0e, + 0x25, 0xd2, 0xc3, 0x0b, 0x6c, 0xd3, 0xc4, 0x6a, 0x2f, 0xb9, 0x09, 0xd3, 0x9f, 0xbb, 0xe3, 0xd4, + 0x74, 0x3e, 0x6a, 0x5c, 0x2d, 0x4a, 0x49, 0x5e, 0x85, 0x21, 0xff, 0x03, 0x30, 0x32, 0x9c, 0x14, + 0xd1, 0x2c, 0x58, 0xb0, 0xe2, 0x89, 0xc4, 0xe5, 0x02, 0xe4, 0x59, 0x18, 0x40, 0x73, 0xa5, 0xa5, + 0xd7, 0xfd, 0xb0, 0x81, 0x3c, 0xce, 0xa0, 0x0f, 0x94, 0xa3, 0x19, 0x04, 0x94, 0xe4, 0x13, 0x50, + 0x88, 0x64, 0xb2, 0xbf, 0xb6, 0x03, 0x07, 0xbf, 0xab, 0x72, 0xdc, 0x5c, 0xbe, 0x01, 0x89, 0x67, + 0xb1, 0x17, 0x4c, 0xc9, 0x22, 0x9c, 0x5c, 0x70, 0xa8, 0x81, 0xae, 0xa4, 0x93, 0xf7, 0x5b, 0x8e, + 0x88, 0x6a, 0xcc, 0x07, 0x30, 0xae, 0x3f, 0x2d, 0x1f, 0xcd, 0x56, 0x46, 0x81, 0x97, 0x63, 0x97, + 0xa5, 0x14, 0x67, 0x46, 0x09, 0x6f, 0xc9, 0x2d, 0xba, 0xb1, 0x8e, 0xd9, 0x4c, 0xfb, 0x43, 0xa3, + 0x44, 0x08, 0x7a, 0x4d, 0xa0, 0x64, 0xa3, 0x24, 0x5a, 0xe8, 0xdc, 0x0b, 0x30, 0xb8, 0xd7, 0xd8, + 0xb3, 0xbf, 0x9e, 0xed, 0xe0, 0xf9, 0xfd, 0xe0, 0xa6, 0xff, 0x08, 0x12, 0xcf, 0xf5, 0x76, 0x48, + 0x3c, 0xf7, 0xed, 0x6c, 0x07, 0xb7, 0xf6, 0x07, 0x3a, 0x41, 0x54, 0x20, 0x8c, 0x68, 0x82, 0xa8, + 0x30, 0x37, 0x97, 0x69, 0x68, 0x32, 0x51, 0x2c, 0x95, 0x5c, 0x61, 0xdb, 0x54, 0x72, 0xbf, 0x94, + 0xeb, 0xe6, 0xf6, 0x7f, 0x2c, 0xfb, 0xdd, 0xc8, 0xfe, 0x3a, 0x0c, 0x06, 0x92, 0xad, 0x94, 0xd1, + 0x9e, 0x19, 0x0e, 0x22, 0x5d, 0x73, 0x30, 0x96, 0x91, 0x88, 0xc8, 0x15, 0xde, 0xd6, 0xaa, 0xf9, + 0x36, 0x8f, 0xb9, 0x3a, 0x2c, 0xa2, 0x69, 0xea, 0x9e, 0x5e, 0x73, 0xcd, 0xb7, 0xa9, 0x16, 0xa0, + 0xd5, 0xff, 0x23, 0x9b, 0xfa, 0x76, 0xe2, 0xb8, 0x8f, 0x76, 0xd1, 0x47, 0x29, 0x42, 0xe4, 0xaf, + 0x3e, 0x8e, 0x85, 0xb8, 0x0b, 0x21, 0xfe, 0x55, 0x36, 0xf5, 0x8d, 0xcc, 0xb1, 0x10, 0x77, 0x33, + 0x5b, 0x3c, 0x09, 0x03, 0x9a, 0xbd, 0xee, 0x62, 0xde, 0x68, 0x31, 0x57, 0xe0, 0x44, 0xed, 0xd8, + 0xeb, 0x2e, 0xcf, 0xa9, 0xad, 0x85, 0x04, 0xea, 0x77, 0xb2, 0x5d, 0x5e, 0x11, 0x1d, 0x0b, 0xfe, + 0xdd, 0x5c, 0x22, 0x7f, 0x2b, 0x1b, 0x79, 0xa5, 0xf4, 0x40, 0x67, 0x5a, 0xad, 0xd6, 0x57, 0x69, + 0x53, 0x8f, 0x67, 0x5a, 0x75, 0x11, 0x2a, 0xf2, 0x9d, 0x85, 0x24, 0xea, 0x57, 0xb3, 0xb1, 0x67, + 0x5a, 0xc7, 0xb2, 0xdb, 0xb1, 0xec, 0x02, 0xad, 0x13, 0x2f, 0xcf, 0x8e, 0x25, 0xb7, 0x53, 0xc9, + 0x7d, 0x26, 0x1b, 0x7b, 0xa4, 0xf7, 0xc0, 0xca, 0x8e, 0x0d, 0xc0, 0xe4, 0xe3, 0xc1, 0x07, 0x56, + 0x93, 0x9e, 0x84, 0x01, 0x21, 0x87, 0x60, 0xa9, 0xe0, 0xf3, 0x3e, 0x07, 0xe2, 0x01, 0x6a, 0x40, + 0xa0, 0xfe, 0x40, 0x16, 0xa2, 0x8f, 0x27, 0x1f, 0x50, 0x1d, 0xfa, 0xad, 0x6c, 0xf4, 0xd9, 0xe8, + 0x83, 0xab, 0x3f, 0x57, 0x01, 0xaa, 0xed, 0xa5, 0xba, 0x88, 0x3a, 0xd8, 0x2b, 0x9d, 0xc0, 0x07, + 0x50, 0x4d, 0xa2, 0x50, 0xff, 0x7d, 0x36, 0xf5, 0x2d, 0xeb, 0x83, 0x2b, 0xc0, 0x67, 0xf0, 0x54, + 0xbc, 0x6e, 0x85, 0x13, 0x39, 0x1e, 0x42, 0xb2, 0xf1, 0x97, 0x48, 0x76, 0xe2, 0x13, 0x92, 0x0f, + 0xa5, 0x98, 0x6b, 0x18, 0x8a, 0x35, 0x34, 0xd7, 0xe4, 0x1b, 0x06, 0xc9, 0x70, 0xfb, 0x7f, 0xb3, + 0xdb, 0x3d, 0xfd, 0x7d, 0x90, 0x57, 0xd5, 0xbe, 0x05, 0x7d, 0x03, 0x43, 0x54, 0xb1, 0x9e, 0x18, + 0xe2, 0xa9, 0x38, 0x5a, 0x1c, 0x24, 0x5f, 0xab, 0x09, 0x2a, 0xf5, 0x2f, 0x7b, 0xd3, 0xdf, 0x9d, + 0x3e, 0xb8, 0x22, 0x3c, 0x0f, 0xf9, 0x05, 0xdd, 0x5b, 0x15, 0x9a, 0x8c, 0xb7, 0x75, 0x2d, 0xdd, + 0x5b, 0xd5, 0x10, 0x4a, 0xae, 0x40, 0xbf, 0xa6, 0xaf, 0xf3, 0x33, 0xcf, 0x42, 0x98, 0x26, 0xc5, + 0xd1, 0xd7, 0x6b, 0xfc, 0xdc, 0x33, 0x40, 0x13, 0x35, 0x48, 0xd3, 0xc3, 0x4f, 0xbe, 0x31, 0x47, + 0x04, 0x4f, 0xd3, 0x13, 0x24, 0xe7, 0x39, 0x0f, 0xf9, 0x71, 0xdb, 0xd8, 0x40, 0x8f, 0x98, 0x21, + 0x5e, 0xd9, 0x92, 0x6d, 0x6c, 0x68, 0x08, 0x25, 0x9f, 0xcd, 0x40, 0xdf, 0x34, 0xd5, 0x0d, 0x36, + 0x42, 0x06, 0xba, 0x39, 0x94, 0x7c, 0xe4, 0x60, 0x1c, 0x4a, 0xc6, 0x56, 0x79, 0x65, 0xb2, 0xa2, + 0x88, 0xfa, 0xc9, 0x4d, 0xe8, 0x9f, 0xd0, 0x3d, 0xba, 0x62, 0x3b, 0x1b, 0xe8, 0x22, 0x33, 0x12, + 0xfa, 0x2e, 0x46, 0xf4, 0xc7, 0x27, 0xe2, 0x37, 0x63, 0x75, 0xf1, 0x4b, 0x0b, 0x0a, 0x33, 0xb1, + 0x88, 0xc4, 0xa1, 0x83, 0xa1, 0x58, 0x78, 0x86, 0xd0, 0x20, 0x3f, 0x68, 0x70, 0xac, 0x3c, 0x94, + 0x7e, 0xac, 0x8c, 0xd6, 0x23, 0xba, 0xd1, 0x61, 0x72, 0x9c, 0x61, 0x5c, 0xf4, 0xb9, 0xf5, 0x88, + 0x50, 0xcc, 0x8d, 0xa3, 0x49, 0x24, 0xea, 0x37, 0x7b, 0x21, 0xf5, 0x95, 0xda, 0xb1, 0x92, 0x1f, + 0x2b, 0x79, 0xa8, 0xe4, 0xe5, 0x84, 0x92, 0x9f, 0x4b, 0xbe, 0x7b, 0x7c, 0x8f, 0x6a, 0xf8, 0x97, + 0xf3, 0x89, 0x57, 0xd3, 0x0f, 0xf6, 0xee, 0x32, 0x94, 0x5e, 0xef, 0xb6, 0xd2, 0x0b, 0x06, 0x44, + 0x61, 0xdb, 0x01, 0xd1, 0xb7, 0xd3, 0x01, 0xd1, 0xdf, 0x71, 0x40, 0x84, 0x0a, 0x32, 0xd0, 0x51, + 0x41, 0x2a, 0x62, 0xd0, 0x40, 0xf7, 0xc4, 0x6b, 0xe7, 0xb7, 0x36, 0x8b, 0x23, 0x6c, 0x34, 0xa5, + 0xa6, 0x5c, 0x43, 0x16, 0xea, 0x37, 0xf2, 0x5d, 0x42, 0x1d, 0x1c, 0x8a, 0x8e, 0x3c, 0x03, 0xb9, + 0x52, 0xab, 0x25, 0xf4, 0xe3, 0xa4, 0x14, 0x65, 0xa1, 0x43, 0x29, 0x46, 0x4d, 0x5e, 0x84, 0x5c, + 0xe9, 0x6e, 0x35, 0x1e, 0xb0, 0xbd, 0x74, 0xb7, 0x2a, 0xbe, 0xa4, 0x63, 0xd9, 0xbb, 0x55, 0xf2, + 0x72, 0x18, 0x39, 0x6d, 0xb5, 0x6d, 0xad, 0x89, 0x8d, 0xa2, 0xf0, 0xa4, 0xf5, 0x3d, 0x6d, 0xea, + 0x0c, 0xc5, 0xb6, 0x8b, 0x31, 0xda, 0x98, 0x36, 0x15, 0x76, 0xae, 0x4d, 0x7d, 0xdb, 0x6a, 0x53, + 0xff, 0x4e, 0xb5, 0x69, 0x60, 0x07, 0xda, 0x04, 0xdb, 0x6a, 0xd3, 0xe0, 0xfe, 0xb5, 0xa9, 0x05, + 0xe7, 0x92, 0xe1, 0x69, 0x02, 0x8d, 0xd0, 0x80, 0x24, 0xb1, 0xc2, 0xb1, 0x04, 0xaf, 0xfe, 0xdb, + 0x1c, 0x5b, 0xe3, 0x09, 0x7e, 0xe3, 0xe9, 0x71, 0xb5, 0x94, 0xd2, 0xea, 0xaf, 0x67, 0x3b, 0x47, + 0xd5, 0x39, 0x9a, 0x53, 0xdc, 0xf7, 0xa4, 0x4a, 0x29, 0x1f, 0x7d, 0xe5, 0xd8, 0x59, 0xca, 0x31, + 0xb6, 0x69, 0x32, 0xfb, 0x7a, 0xa6, 0x53, 0xa8, 0x9f, 0x7d, 0x49, 0xec, 0xf1, 0xa4, 0xb3, 0x1a, + 0xba, 0xe0, 0xbb, 0x51, 0x2f, 0xb5, 0x78, 0xbe, 0xd8, 0xdc, 0x1e, 0xf3, 0xc5, 0xfe, 0x5e, 0x06, + 0x4e, 0xde, 0x6a, 0x2f, 0x51, 0xe1, 0x9c, 0x16, 0x34, 0xe3, 0x2d, 0x00, 0x06, 0x16, 0x4e, 0x2c, + 0x19, 0x74, 0x62, 0xf9, 0x80, 0x1c, 0xa6, 0x27, 0x56, 0xe0, 0x6a, 0x48, 0xcd, 0x1d, 0x58, 0x2e, + 0xf8, 0x7e, 0x9a, 0x6b, 0xed, 0x25, 0x5a, 0x4b, 0x78, 0xb2, 0x48, 0xdc, 0xcf, 0xbd, 0xc2, 0x3d, + 0xe0, 0xf7, 0xea, 0x34, 0xf2, 0xab, 0xd9, 0x8e, 0x91, 0x91, 0x8e, 0x6c, 0x62, 0x9a, 0x8f, 0xa5, + 0xf6, 0x4a, 0x3c, 0x41, 0x4d, 0x0a, 0x49, 0x8c, 0x63, 0x1a, 0x97, 0x74, 0x81, 0x1d, 0xf1, 0x74, + 0x49, 0xef, 0xaa, 0xc0, 0xfe, 0x38, 0xd3, 0x31, 0x82, 0xd5, 0x51, 0x15, 0x98, 0xfa, 0x4f, 0x73, + 0x7e, 0xe0, 0xac, 0x7d, 0x7d, 0xc2, 0x93, 0x30, 0x20, 0xde, 0x0f, 0x46, 0x7d, 0x6b, 0xc5, 0x51, + 0x1e, 0x1e, 0x0d, 0x07, 0x04, 0x6c, 0x99, 0x97, 0x1c, 0x7f, 0x25, 0xdf, 0x5a, 0xc9, 0xe9, 0x57, + 0x93, 0x48, 0xd8, 0x42, 0x3e, 0x79, 0xdf, 0xf4, 0xd0, 0x2a, 0x60, 0x7d, 0x99, 0xe3, 0x0b, 0x39, + 0xbd, 0x6f, 0x7a, 0xdc, 0x26, 0x08, 0xd0, 0x6c, 0x91, 0xae, 0x86, 0xc9, 0x20, 0xc5, 0x22, 0xed, + 0x8a, 0x9c, 0x98, 0xe2, 0x45, 0xd8, 0x93, 0x30, 0x20, 0x1c, 0x56, 0x85, 0x9b, 0x89, 0x68, 0xad, + 0x70, 0x71, 0xc5, 0xd6, 0x06, 0x04, 0x8c, 0xa3, 0x46, 0x57, 0x42, 0xc7, 0x3a, 0xe4, 0xe8, 0x20, + 0x44, 0x13, 0x18, 0x72, 0x1d, 0x46, 0xaa, 0x9e, 0x6e, 0x19, 0xba, 0x63, 0xcc, 0xb7, 0xbd, 0x56, + 0xdb, 0x93, 0x8d, 0x52, 0xd7, 0x33, 0xec, 0xb6, 0xa7, 0xc5, 0x28, 0xc8, 0x07, 0x61, 0xd8, 0x87, + 0x4c, 0x3a, 0x8e, 0xed, 0xc8, 0x96, 0x87, 0xeb, 0x19, 0xd4, 0x71, 0xb4, 0x28, 0x01, 0xf9, 0x10, + 0x0c, 0x57, 0xac, 0x7b, 0x36, 0xcf, 0x32, 0x7a, 0x5b, 0x9b, 0x11, 0x76, 0x08, 0xbe, 0xb2, 0x32, + 0x03, 0x44, 0xad, 0xed, 0x34, 0xb4, 0x28, 0xa1, 0xba, 0x95, 0x4d, 0xc6, 0x17, 0x7b, 0x70, 0x37, + 0x2d, 0x57, 0xa2, 0xce, 0x74, 0xe8, 0x41, 0x8a, 0x06, 0xa1, 0xec, 0xcb, 0xcb, 0xed, 0xc2, 0xeb, + 0xd0, 0x7f, 0x8b, 0x6e, 0x70, 0xbf, 0xcf, 0x42, 0xe8, 0x2a, 0xbc, 0x26, 0x60, 0xf2, 0x89, 0xab, + 0x4f, 0xa7, 0x7e, 0x2d, 0x9b, 0x8c, 0x9c, 0xf6, 0xe0, 0x0a, 0xfb, 0x83, 0xd0, 0x87, 0xa2, 0xac, + 0xf8, 0x47, 0xfe, 0x28, 0x40, 0x14, 0x77, 0xd4, 0x03, 0xd9, 0x27, 0x53, 0x7f, 0xb6, 0x10, 0x0f, + 0xa7, 0xf7, 0xe0, 0x4a, 0xef, 0x25, 0x18, 0x9c, 0xb0, 0x2d, 0xd7, 0x74, 0x3d, 0x6a, 0xd5, 0x7d, + 0x85, 0x7d, 0x88, 0x19, 0x54, 0xf5, 0x10, 0x2c, 0x3f, 0x2f, 0x92, 0xa8, 0xf7, 0xa2, 0xbc, 0xe4, + 0x39, 0x18, 0x40, 0x91, 0xa3, 0x9f, 0xb4, 0x94, 0xc5, 0x7c, 0x89, 0x01, 0xe3, 0x4e, 0xd2, 0x21, + 0x29, 0xb9, 0x0d, 0xfd, 0x13, 0xab, 0x66, 0xc3, 0x70, 0xa8, 0x85, 0xfe, 0xc2, 0xd2, 0xab, 0xe5, + 0x68, 0x5f, 0x5e, 0xc5, 0x7f, 0x91, 0x96, 0x37, 0xa7, 0x2e, 0x8a, 0x45, 0x1e, 0x58, 0x09, 0xd8, + 0xb9, 0x1f, 0xcb, 0x02, 0x84, 0x05, 0xc8, 0x23, 0x90, 0x0d, 0xf2, 0xac, 0xa1, 0x9b, 0x4a, 0x44, + 0x83, 0xb2, 0xb8, 0x54, 0x88, 0xb1, 0x9d, 0xdd, 0x76, 0x6c, 0xdf, 0x86, 0x02, 0x3f, 0xf1, 0x42, + 0x4f, 0x72, 0x29, 0xc2, 0x57, 0xc7, 0x06, 0x5f, 0x45, 0x7a, 0xbe, 0x99, 0x45, 0xcb, 0x33, 0xe2, + 0x95, 0xcd, 0x99, 0x9d, 0xab, 0x43, 0x2f, 0xfe, 0x45, 0x2e, 0x41, 0x1e, 0xa5, 0x98, 0xc1, 0x7d, + 0x2c, 0xce, 0xd2, 0x31, 0xf9, 0x21, 0x9e, 0x75, 0xd3, 0x84, 0x6d, 0x79, 0xac, 0x6a, 0x6c, 0xf5, + 0x90, 0x90, 0x8b, 0x80, 0x45, 0xe4, 0x22, 0x60, 0xea, 0xff, 0x93, 0x4d, 0x09, 0xf4, 0xf8, 0xe0, + 0x0e, 0x93, 0x17, 0x00, 0xf0, 0xb5, 0x36, 0x93, 0xa7, 0xff, 0x44, 0x03, 0x47, 0x09, 0x32, 0x42, + 0xb5, 0x8d, 0x6c, 0x3b, 0x42, 0x62, 0xf5, 0x0f, 0x32, 0x89, 0xe8, 0x80, 0xfb, 0x92, 0xa3, 0x6c, + 0x95, 0x65, 0xf7, 0x68, 0xc6, 0xfa, 0x7d, 0x91, 0xdb, 0x5d, 0x5f, 0x44, 0xbf, 0xe5, 0x00, 0x2c, + 0xd3, 0xc3, 0xfc, 0x96, 0x6f, 0x66, 0xd3, 0x62, 0x25, 0x1e, 0x4d, 0x15, 0xbf, 0x11, 0x18, 0xa5, + 0xf9, 0x9d, 0x64, 0x28, 0x17, 0x66, 0xea, 0x27, 0xe1, 0x44, 0x2c, 0x82, 0xa0, 0x48, 0x7e, 0x78, + 0xa9, 0x7b, 0x28, 0xc2, 0xce, 0xef, 0xfc, 0x23, 0x64, 0xea, 0x7f, 0xc8, 0x74, 0x8f, 0x1f, 0x79, + 0xe8, 0xaa, 0x93, 0x22, 0x80, 0xdc, 0xdf, 0x8d, 0x00, 0x0e, 0x60, 0x1b, 0x7c, 0xb4, 0x05, 0xf0, + 0x1e, 0x99, 0x3c, 0xde, 0x6d, 0x01, 0xfc, 0x6c, 0x66, 0xdb, 0xf0, 0x9f, 0x87, 0x2d, 0x03, 0xf5, + 0x1f, 0x65, 0x52, 0xc3, 0x74, 0xee, 0xab, 0x5d, 0x2f, 0x43, 0x81, 0xbb, 0xd5, 0x88, 0x56, 0x49, + 0x89, 0x4d, 0x18, 0xb4, 0x43, 0x79, 0x51, 0x86, 0xcc, 0x40, 0x1f, 0x6f, 0x83, 0x11, 0x4f, 0x00, + 0x9c, 0xd2, 0x4e, 0xa3, 0xd3, 0xe4, 0x28, 0xd0, 0xea, 0xef, 0x67, 0x12, 0x51, 0x43, 0x0f, 0xf1, + 0xdb, 0xc2, 0xa9, 0x3a, 0xb7, 0xf3, 0xa9, 0x5a, 0xfd, 0x8b, 0x6c, 0x7a, 0xd0, 0xd2, 0x43, 0xfc, + 0x90, 0x83, 0x38, 0x4e, 0xdb, 0xdb, 0xba, 0xb5, 0x08, 0x23, 0x51, 0x59, 0x88, 0x65, 0xeb, 0x62, + 0x7a, 0xe8, 0xd6, 0x0e, 0xad, 0x88, 0xf1, 0x50, 0xdf, 0xc9, 0x24, 0xe3, 0xad, 0x1e, 0xfa, 0xfc, + 0xb4, 0x37, 0x6d, 0x89, 0x7e, 0xca, 0x7b, 0x64, 0xad, 0x39, 0x88, 0x4f, 0x79, 0x8f, 0xac, 0x1a, + 0x7b, 0xfb, 0x94, 0x5f, 0xce, 0x76, 0x0a, 0x57, 0x7b, 0xe8, 0x1f, 0xf4, 0x51, 0x59, 0xc8, 0xbc, + 0x65, 0xe2, 0xd3, 0x1e, 0xe9, 0x14, 0x1f, 0xb6, 0x03, 0xcf, 0x04, 0x9f, 0xbd, 0x8d, 0xf1, 0x54, + 0x61, 0xbd, 0x47, 0x14, 0xf9, 0x68, 0x08, 0xeb, 0x3d, 0x32, 0x54, 0xde, 0x7b, 0xc2, 0xfa, 0x9d, + 0xec, 0x4e, 0x63, 0x24, 0x1f, 0x0b, 0x2f, 0x21, 0xbc, 0x2f, 0x66, 0x93, 0xb1, 0xbb, 0x0f, 0x5d, + 0x4c, 0x53, 0x50, 0x10, 0x51, 0xc4, 0x3b, 0x0a, 0x87, 0xe3, 0x3b, 0x59, 0x34, 0xe2, 0x3b, 0x6e, + 0x80, 0xb8, 0xc8, 0xd9, 0x99, 0x48, 0x38, 0xad, 0xfa, 0x9d, 0x4c, 0x2c, 0xd0, 0xf5, 0xa1, 0x1c, + 0x21, 0xec, 0x69, 0x49, 0x22, 0xaf, 0xf8, 0x87, 0x99, 0xf9, 0x58, 0xa0, 0xd1, 0xe0, 0x7b, 0xca, + 0xd4, 0xd3, 0xcd, 0x46, 0xbc, 0xbc, 0x88, 0x09, 0xf0, 0xb5, 0x2c, 0x8c, 0x25, 0x48, 0xc9, 0xa5, + 0x48, 0x94, 0x1c, 0x3c, 0x96, 0x8c, 0x39, 0x8f, 0xf3, 0x78, 0x39, 0xbb, 0x38, 0x49, 0xbd, 0x04, + 0xf9, 0xb2, 0xbe, 0xc1, 0xbf, 0xad, 0x97, 0xb3, 0x34, 0xf4, 0x0d, 0xf9, 0xc4, 0x0d, 0xf1, 0x64, + 0x09, 0x4e, 0xf3, 0xfb, 0x10, 0xd3, 0xb6, 0x16, 0xcd, 0x26, 0xad, 0x58, 0xb3, 0x66, 0xa3, 0x61, + 0xba, 0xe2, 0x52, 0xef, 0xc9, 0xad, 0xcd, 0xe2, 0x65, 0xcf, 0xf6, 0xf4, 0x46, 0x8d, 0xfa, 0x64, + 0x35, 0xcf, 0x6c, 0xd2, 0x9a, 0x69, 0xd5, 0x9a, 0x48, 0x29, 0xb1, 0x4c, 0x67, 0x45, 0x2a, 0x3c, + 0xa6, 0x6c, 0xb5, 0xae, 0x5b, 0x16, 0x35, 0x2a, 0xd6, 0xf8, 0x86, 0x47, 0xf9, 0x65, 0x60, 0x8e, + 0x1f, 0x09, 0xf2, 0xb7, 0xe1, 0x1c, 0xcd, 0x18, 0x2f, 0x31, 0x02, 0x2d, 0xa5, 0x90, 0xfa, 0xbb, + 0xf9, 0x94, 0x18, 0xe7, 0x47, 0x48, 0x7d, 0xfc, 0x9e, 0xce, 0x6f, 0xd3, 0xd3, 0xd7, 0xa0, 0xef, + 0x0e, 0x75, 0xf0, 0x7c, 0x8b, 0x5f, 0x30, 0xa0, 0x33, 0xfb, 0x3d, 0x0e, 0x92, 0x6f, 0x68, 0x04, + 0x15, 0x69, 0xc0, 0xb9, 0x45, 0xd6, 0x4d, 0xe9, 0x9d, 0x59, 0xd8, 0x43, 0x67, 0x76, 0xe1, 0x47, + 0xde, 0x84, 0xb3, 0x88, 0x4d, 0xe9, 0xd6, 0x3e, 0xac, 0x0a, 0xc3, 0x4f, 0xf1, 0xaa, 0xd2, 0x3b, + 0xb7, 0x53, 0x79, 0xf2, 0x51, 0x18, 0x0a, 0x06, 0x88, 0x49, 0x5d, 0x71, 0x73, 0xd1, 0x65, 0x9c, + 0xf1, 0xd8, 0x6e, 0x0c, 0x8c, 0x2e, 0x64, 0xd1, 0xf8, 0x60, 0x11, 0x5e, 0xea, 0x3f, 0xcc, 0x74, + 0x8b, 0xb5, 0x7e, 0xe8, 0xb3, 0xf2, 0x2b, 0xd0, 0x67, 0xf0, 0x8f, 0x12, 0x3a, 0xd5, 0x3d, 0x1a, + 0x3b, 0x27, 0xd5, 0xfc, 0x32, 0xea, 0x9f, 0x67, 0xba, 0x86, 0x78, 0x3f, 0xea, 0x9f, 0xf7, 0xc5, + 0x5c, 0x87, 0xcf, 0x13, 0x93, 0xe8, 0x15, 0x18, 0x35, 0xc3, 0x18, 0xb4, 0xb5, 0x30, 0xfc, 0x94, + 0x76, 0x42, 0x82, 0xe3, 0xe8, 0xba, 0x01, 0x67, 0x7c, 0xc7, 0x47, 0xc7, 0xf7, 0x10, 0x73, 0x6b, + 0x6d, 0xc7, 0xe4, 0xe3, 0x52, 0x3b, 0xe5, 0xc6, 0xdc, 0xc7, 0xdc, 0xdb, 0x8e, 0xc9, 0x2a, 0xd0, + 0xbd, 0x55, 0x6a, 0xe9, 0xb5, 0x75, 0xdb, 0x59, 0xc3, 0x00, 0xa2, 0x7c, 0x70, 0x6a, 0x27, 0x38, + 0xfc, 0xae, 0x0f, 0x26, 0x8f, 0xc1, 0xf0, 0x4a, 0xa3, 0x4d, 0x83, 0x90, 0x8d, 0xfc, 0xae, 0x4f, + 0x1b, 0x62, 0xc0, 0xe0, 0x86, 0xe4, 0x02, 0x00, 0x12, 0x79, 0x18, 0x80, 0x1f, 0x2f, 0xf6, 0xb4, + 0x01, 0x06, 0x59, 0x14, 0xdd, 0x75, 0x8e, 0x6b, 0x35, 0x17, 0x52, 0xad, 0x61, 0x5b, 0x2b, 0x35, + 0x8f, 0x3a, 0x4d, 0x6c, 0x28, 0x3a, 0x33, 0x68, 0x67, 0x90, 0x02, 0xaf, 0x4e, 0xdc, 0x19, 0xdb, + 0x5a, 0x59, 0xa4, 0x4e, 0x93, 0x35, 0xf5, 0x49, 0x20, 0xa2, 0xa9, 0x0e, 0x1e, 0x7a, 0xf0, 0x8f, + 0x43, 0x6f, 0x06, 0x4d, 0x7c, 0x04, 0x3f, 0x0d, 0xc1, 0x0f, 0x2b, 0xc2, 0x20, 0x8f, 0x5b, 0xc7, + 0x85, 0x86, 0x2e, 0x0c, 0x1a, 0x70, 0x10, 0xca, 0xeb, 0x0c, 0x08, 0xef, 0x0a, 0xee, 0xd5, 0xad, + 0x89, 0x5f, 0xea, 0xe7, 0x72, 0x69, 0x51, 0xe9, 0xf7, 0xa5, 0x68, 0xe1, 0xb4, 0x9a, 0xdd, 0xd5, + 0xb4, 0x7a, 0xc2, 0x6a, 0x37, 0x6b, 0x7a, 0xab, 0x55, 0x5b, 0x36, 0x1b, 0xf8, 0xac, 0x0a, 0x17, + 0x3e, 0x6d, 0xd8, 0x6a, 0x37, 0x4b, 0xad, 0xd6, 0x14, 0x07, 0x92, 0x27, 0x60, 0x8c, 0xd1, 0x61, + 0x27, 0x05, 0x94, 0x79, 0xa4, 0x64, 0x0c, 0x30, 0xf0, 0xab, 0x4f, 0xfb, 0x10, 0xf4, 0x0b, 0x9e, + 0x7c, 0xad, 0xea, 0xd5, 0xfa, 0x38, 0x33, 0x97, 0xf5, 0x5c, 0xc0, 0x86, 0x4f, 0xae, 0xbd, 0xda, + 0x80, 0x5f, 0x1e, 0xc3, 0x1b, 0x5b, 0xed, 0x26, 0x8f, 0x88, 0xd5, 0x87, 0xc8, 0xe0, 0x37, 0xb9, + 0x04, 0x23, 0x8c, 0x4b, 0x20, 0x30, 0x1e, 0x11, 0xb6, 0x57, 0x8b, 0x41, 0xc9, 0x75, 0x38, 0x15, + 0x81, 0x70, 0x1b, 0x94, 0x3f, 0x13, 0xe8, 0xd5, 0x52, 0x71, 0xea, 0x57, 0x73, 0xd1, 0x58, 0xf9, + 0x87, 0xd0, 0x11, 0x67, 0xa1, 0xcf, 0x76, 0x56, 0x6a, 0x6d, 0xa7, 0x21, 0xc6, 0x5e, 0xc1, 0x76, + 0x56, 0x6e, 0x3b, 0x0d, 0x72, 0x1a, 0x0a, 0xac, 0x77, 0x4c, 0x43, 0x0c, 0xb1, 0x5e, 0xbd, 0xd5, + 0xaa, 0x18, 0xa4, 0xc4, 0x3b, 0x04, 0xa3, 0x89, 0xd6, 0xea, 0xb8, 0xb5, 0xe7, 0x4e, 0x09, 0xbd, + 0x7c, 0xc5, 0x4b, 0x20, 0xb1, 0x9f, 0x30, 0xc6, 0x28, 0x3f, 0x08, 0x88, 0xb1, 0x30, 0x70, 0x5b, + 0x62, 0xf0, 0x3e, 0x89, 0xb3, 0x10, 0xc8, 0x90, 0x05, 0xdf, 0xc4, 0x18, 0xa4, 0x0c, 0x24, 0xa4, + 0x6a, 0xda, 0x86, 0xb9, 0x6c, 0x52, 0xfe, 0xaa, 0xa3, 0x97, 0x5f, 0xfc, 0x26, 0xb1, 0xda, 0xa8, + 0xcf, 0x64, 0x56, 0x40, 0xc8, 0x4b, 0x5c, 0x09, 0x39, 0x1d, 0xae, 0x7d, 0xbc, 0x6f, 0xb9, 0x9d, + 0x16, 0x43, 0xa1, 0x66, 0x62, 0x79, 0x5c, 0x08, 0xd5, 0x77, 0x72, 0xc9, 0x84, 0x09, 0x87, 0x62, + 0xd7, 0x4c, 0x03, 0x88, 0x7c, 0x28, 0xe1, 0xe5, 0x5a, 0xe0, 0x71, 0x1e, 0x62, 0x3a, 0xf0, 0x90, + 0xca, 0x92, 0x2b, 0xd0, 0xcf, 0xbf, 0xa8, 0x52, 0x16, 0xf6, 0x0e, 0xba, 0x88, 0xb9, 0x2d, 0x73, + 0x79, 0x19, 0xfd, 0xc9, 0x02, 0x34, 0xb9, 0x04, 0x7d, 0xe5, 0xb9, 0x6a, 0xb5, 0x34, 0xe7, 0xdf, + 0x14, 0xe3, 0xfb, 0x12, 0xc3, 0x72, 0x6b, 0xae, 0x6e, 0xb9, 0x9a, 0x8f, 0x24, 0x8f, 0x41, 0xa1, + 0xb2, 0x80, 0x64, 0xfc, 0xd5, 0xe4, 0xe0, 0xd6, 0x66, 0xb1, 0xcf, 0x6c, 0x71, 0x2a, 0x81, 0xc2, + 0x7a, 0xef, 0x54, 0xca, 0x92, 0xbb, 0x04, 0xaf, 0xf7, 0x9e, 0x69, 0xe0, 0xb5, 0xb3, 0x16, 0xa0, + 0xc9, 0xb3, 0x30, 0x54, 0xa5, 0x8e, 0xa9, 0x37, 0xe6, 0xda, 0xb8, 0x55, 0xe4, 0x2e, 0x62, 0x63, + 0x5b, 0x9b, 0xc5, 0x61, 0x17, 0xe1, 0x35, 0x0b, 0x11, 0x5a, 0x84, 0x8c, 0x9c, 0x87, 0xfc, 0xb4, + 0x69, 0xf9, 0x4f, 0x18, 0xd0, 0xc7, 0x7d, 0xd5, 0xb4, 0x3c, 0x0d, 0xa1, 0xea, 0xbf, 0xca, 0xa6, + 0x67, 0x9d, 0x38, 0x84, 0xe1, 0xb8, 0xc7, 0x9b, 0xde, 0x98, 0x12, 0xe4, 0xf7, 0xa1, 0x04, 0xcb, + 0x70, 0xa2, 0x64, 0x34, 0x4d, 0xab, 0x84, 0x3f, 0xdd, 0xd9, 0xa9, 0x12, 0x0e, 0x6f, 0xe9, 0x09, + 0x5d, 0x0c, 0x2d, 0xbe, 0x87, 0xc7, 0xdb, 0x65, 0xa8, 0x9a, 0xce, 0x71, 0xb5, 0xe6, 0xb2, 0x5e, + 0xab, 0xf3, 0x84, 0x0d, 0x5a, 0x9c, 0xa9, 0xfa, 0xa3, 0xd9, 0x6d, 0x12, 0x65, 0x3c, 0x88, 0xd2, + 0x57, 0xbf, 0x94, 0xed, 0x9e, 0xab, 0xe4, 0x81, 0x14, 0xca, 0x5f, 0x65, 0x53, 0x32, 0x87, 0xec, + 0x4b, 0x12, 0x57, 0xa0, 0x9f, 0xb3, 0x09, 0x5c, 0x6d, 0x71, 0xc6, 0xe1, 0xca, 0x8a, 0x33, 0x9d, + 0x8f, 0x26, 0x73, 0x70, 0xaa, 0xb4, 0xbc, 0x4c, 0xeb, 0x5e, 0x18, 0x79, 0x79, 0x2e, 0x0c, 0x94, + 0xca, 0x23, 0xcd, 0x0a, 0x7c, 0x18, 0xb9, 0x19, 0x03, 0x82, 0xa4, 0x96, 0x23, 0x8b, 0x70, 0x26, + 0x0e, 0xaf, 0x72, 0x33, 0x3d, 0x2f, 0x05, 0x9f, 0x4d, 0x70, 0xe4, 0xff, 0x69, 0x1d, 0xca, 0xa6, + 0xb5, 0x12, 0xa7, 0xd3, 0xde, 0x6e, 0xad, 0xc4, 0xb9, 0x35, 0xb5, 0x9c, 0xfa, 0xb5, 0x9c, 0x9c, + 0x60, 0xe5, 0xc1, 0x75, 0x8a, 0xba, 0x11, 0x71, 0x85, 0xde, 0xe9, 0x90, 0x79, 0x56, 0x44, 0xf9, + 0x30, 0xda, 0x8e, 0xef, 0x35, 0x18, 0x44, 0x19, 0x40, 0xa0, 0xec, 0xff, 0x17, 0x50, 0x92, 0x0a, + 0xe4, 0x4b, 0xce, 0x0a, 0x37, 0x41, 0xb7, 0x7b, 0xf8, 0xa4, 0x3b, 0x2b, 0x6e, 0xfa, 0xc3, 0x27, + 0xc6, 0x42, 0xfd, 0x91, 0x6c, 0x97, 0x9c, 0x28, 0x0f, 0xe2, 0x24, 0xf2, 0xc4, 0x2c, 0x0f, 0x72, + 0x7c, 0xcb, 0xb4, 0x0c, 0xf2, 0x10, 0x9c, 0xbe, 0x5d, 0x9d, 0xd4, 0x6a, 0xb7, 0x2a, 0x73, 0xe5, + 0xda, 0xed, 0xb9, 0xea, 0xc2, 0xe4, 0x44, 0x65, 0xaa, 0x32, 0x59, 0x1e, 0xed, 0x21, 0x27, 0xe1, + 0x44, 0x88, 0x9a, 0xbe, 0x3d, 0x5b, 0x9a, 0x1b, 0xcd, 0x90, 0x31, 0x18, 0x0e, 0x81, 0xe3, 0xf3, + 0x8b, 0xa3, 0xd9, 0x27, 0xde, 0x0f, 0x83, 0xb8, 0x8b, 0xe3, 0x2b, 0x1a, 0x19, 0x82, 0xfe, 0xf9, + 0xf1, 0xea, 0xa4, 0x76, 0x07, 0x99, 0x00, 0x14, 0xca, 0x93, 0x73, 0x8c, 0x61, 0xe6, 0x89, 0x7f, + 0x9b, 0x01, 0xa8, 0x4e, 0x2d, 0x2e, 0x08, 0xc2, 0x41, 0xe8, 0xab, 0xcc, 0xdd, 0x29, 0xcd, 0x54, + 0x18, 0x5d, 0x3f, 0xe4, 0xe7, 0x17, 0x26, 0x59, 0x0d, 0x03, 0xd0, 0x3b, 0x31, 0x33, 0x5f, 0x9d, + 0x1c, 0xcd, 0x32, 0xa0, 0x36, 0x59, 0x2a, 0x8f, 0xe6, 0x18, 0xf0, 0xae, 0x56, 0x59, 0x9c, 0x1c, + 0xcd, 0xb3, 0x3f, 0x67, 0xaa, 0x8b, 0xa5, 0xc5, 0xd1, 0x5e, 0xf6, 0xe7, 0x14, 0xfe, 0x59, 0x60, + 0xcc, 0xaa, 0x93, 0x8b, 0xf8, 0xa3, 0x8f, 0x35, 0x61, 0xca, 0xff, 0xd5, 0xcf, 0x50, 0x8c, 0x75, + 0xb9, 0xa2, 0x8d, 0x0e, 0xb0, 0x1f, 0x8c, 0x25, 0xfb, 0x01, 0xac, 0x71, 0xda, 0xe4, 0xec, 0xfc, + 0x9d, 0xc9, 0xd1, 0x41, 0xc6, 0x6b, 0xf6, 0x16, 0x03, 0x0f, 0xb1, 0x3f, 0xb5, 0x59, 0xf6, 0xe7, + 0x30, 0xe3, 0xa4, 0x4d, 0x96, 0x66, 0x16, 0x4a, 0x8b, 0xd3, 0xa3, 0x23, 0xac, 0x3d, 0xc8, 0xf3, + 0x04, 0x2f, 0x39, 0x57, 0x9a, 0x9d, 0x1c, 0x1d, 0x15, 0x34, 0xe5, 0x99, 0xca, 0xdc, 0xad, 0xd1, + 0x31, 0x6c, 0xc8, 0x9b, 0xb3, 0xf8, 0x83, 0xb0, 0x02, 0xf8, 0xd7, 0xc9, 0x27, 0x3e, 0x0e, 0x85, + 0xf9, 0x2a, 0xda, 0x6d, 0x67, 0xe1, 0xe4, 0x7c, 0xb5, 0xb6, 0xf8, 0xe6, 0xc2, 0x64, 0x4c, 0xde, + 0x63, 0x30, 0xec, 0x23, 0x66, 0x2a, 0x73, 0xb7, 0x3f, 0xc2, 0xa5, 0xed, 0x83, 0x66, 0x4b, 0x13, + 0xf3, 0xd5, 0xd1, 0x2c, 0xeb, 0x15, 0x1f, 0x74, 0xb7, 0x32, 0x57, 0x9e, 0xbf, 0x5b, 0x1d, 0xcd, + 0x3d, 0x71, 0xcf, 0x4f, 0xdb, 0x3a, 0xef, 0x98, 0x2b, 0xa6, 0x45, 0x2e, 0xc0, 0x43, 0xe5, 0xc9, + 0x3b, 0x95, 0x89, 0xc9, 0xda, 0xbc, 0x56, 0xb9, 0x59, 0x99, 0x8b, 0xd5, 0x74, 0x1a, 0xc6, 0xa2, + 0xe8, 0xd2, 0x42, 0x65, 0x34, 0x43, 0xce, 0x00, 0x89, 0x82, 0x5f, 0x2f, 0xcd, 0x4e, 0x8d, 0x66, + 0x89, 0x02, 0xa7, 0xa2, 0xf0, 0xca, 0xdc, 0xe2, 0xed, 0xb9, 0xc9, 0xd1, 0xdc, 0x13, 0x3f, 0x9f, + 0x81, 0xd3, 0xa9, 0x61, 0x04, 0x88, 0x0a, 0x17, 0x27, 0x67, 0x4a, 0xd5, 0xc5, 0xca, 0x44, 0x75, + 0xb2, 0xa4, 0x4d, 0x4c, 0xd7, 0x26, 0x4a, 0x8b, 0x93, 0x37, 0xe7, 0xb5, 0x37, 0x6b, 0x37, 0x27, + 0xe7, 0x26, 0xb5, 0xd2, 0xcc, 0x68, 0x0f, 0x79, 0x0c, 0x8a, 0x1d, 0x68, 0xaa, 0x93, 0x13, 0xb7, + 0xb5, 0xca, 0xe2, 0x9b, 0xa3, 0x19, 0xf2, 0x28, 0x5c, 0xe8, 0x48, 0xc4, 0x7e, 0x8f, 0x66, 0xc9, + 0x45, 0x38, 0xd7, 0x89, 0xe4, 0x8d, 0x99, 0xd1, 0xdc, 0x13, 0x3f, 0x91, 0x01, 0x92, 0x7c, 0x07, + 0x4e, 0x1e, 0x81, 0xf3, 0x4c, 0x2f, 0x6a, 0x9d, 0x1b, 0xf8, 0x28, 0x5c, 0x48, 0xa5, 0x90, 0x9a, + 0x57, 0x84, 0x87, 0x3b, 0x90, 0x88, 0xc6, 0x9d, 0x07, 0x25, 0x9d, 0x00, 0x9b, 0xf6, 0x9b, 0x19, + 0x38, 0x9d, 0x6a, 0x44, 0x92, 0xcb, 0xf0, 0xbe, 0x52, 0x79, 0x96, 0xf5, 0xcd, 0xc4, 0x62, 0x65, + 0x7e, 0xae, 0x5a, 0x9b, 0x9d, 0x2a, 0xd5, 0x98, 0xf6, 0xdd, 0xae, 0xc6, 0x7a, 0xf3, 0x12, 0xa8, + 0x5d, 0x28, 0x27, 0xa6, 0x4b, 0x73, 0x37, 0xd9, 0xf0, 0x23, 0xef, 0x83, 0x47, 0x3a, 0xd2, 0x4d, + 0xce, 0x95, 0xc6, 0x67, 0x26, 0xcb, 0xa3, 0x59, 0xf2, 0x38, 0x3c, 0xda, 0x91, 0xaa, 0x5c, 0xa9, + 0x72, 0xb2, 0xdc, 0x78, 0xf9, 0x9d, 0x7f, 0x7c, 0xb1, 0xe7, 0x9d, 0x6f, 0x5d, 0xcc, 0xfc, 0xd1, + 0xb7, 0x2e, 0x66, 0xfe, 0xe2, 0x5b, 0x17, 0x33, 0x1f, 0xbd, 0xbe, 0x9b, 0xf7, 0xfd, 0x7c, 0xda, + 0x5a, 0x2a, 0xe0, 0x84, 0xfe, 0xcc, 0x7f, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x72, 0xfa, 0x33, 0x89, + 0x04, 0x58, 0x01, 0x00, } func (m *Metadata) Marshal() (dAtA []byte, err error) { @@ -14732,6 +14746,20 @@ func (m *UserMetadata) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.BotInstanceID) > 0 { + i -= len(m.BotInstanceID) + copy(dAtA[i:], m.BotInstanceID) + i = encodeVarintEvents(dAtA, i, uint64(len(m.BotInstanceID))) + i-- + dAtA[i] = 0x62 + } + if len(m.BotName) > 0 { + i -= len(m.BotName) + copy(dAtA[i:], m.BotName) + i = encodeVarintEvents(dAtA, i, uint64(len(m.BotName))) + i-- + dAtA[i] = 0x5a + } if m.UserKind != 0 { i = encodeVarintEvents(dAtA, i, uint64(m.UserKind)) i-- @@ -23679,6 +23707,13 @@ func (m *BotJoin) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.BotInstanceID) > 0 { + i -= len(m.BotInstanceID) + copy(dAtA[i:], m.BotInstanceID) + i = encodeVarintEvents(dAtA, i, uint64(len(m.BotInstanceID))) + i-- + dAtA[i] = 0x4a + } { size, err := m.ConnectionMetadata.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -28717,6 +28752,15 @@ func (m *Identity) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.BotInstanceID) > 0 { + i -= len(m.BotInstanceID) + copy(dAtA[i:], m.BotInstanceID) + i = encodeVarintEvents(dAtA, i, uint64(len(m.BotInstanceID))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xea + } if m.DeviceExtensions != nil { { size, err := m.DeviceExtensions.MarshalToSizedBuffer(dAtA[:i]) @@ -34238,6 +34282,14 @@ func (m *UserMetadata) Size() (n int) { if m.UserKind != 0 { n += 1 + sovEvents(uint64(m.UserKind)) } + l = len(m.BotName) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.BotInstanceID) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -37461,6 +37513,10 @@ func (m *BotJoin) Size() (n int) { } l = m.ConnectionMetadata.Size() n += 1 + l + sovEvents(uint64(l)) + l = len(m.BotInstanceID) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -40013,6 +40069,10 @@ func (m *Identity) Size() (n int) { l = m.DeviceExtensions.Size() n += 2 + l + sovEvents(uint64(l)) } + l = len(m.BotInstanceID) + if l > 0 { + n += 2 + l + sovEvents(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -42600,6 +42660,70 @@ func (m *UserMetadata) Unmarshal(dAtA []byte) error { break } } + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BotName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BotName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BotInstanceID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BotInstanceID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) @@ -69588,6 +69712,38 @@ func (m *BotJoin) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BotInstanceID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BotInstanceID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) @@ -79591,6 +79747,38 @@ func (m *Identity) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 29: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BotInstanceID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BotInstanceID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) diff --git a/api/types/provisioning.go b/api/types/provisioning.go index cfdc48a9ccbcc..8087c85335903 100644 --- a/api/types/provisioning.go +++ b/api/types/provisioning.go @@ -121,6 +121,8 @@ type ProvisionToken interface { GetAllowRules() []*TokenRule // SetAllowRules sets the allow rules SetAllowRules([]*TokenRule) + // GetGCPRules will return the GCP rules within this token. + GetGCPRules() *ProvisionTokenSpecV2GCP // GetAWSIIDTTL returns the TTL of EC2 IIDs GetAWSIIDTTL() Duration // GetJoinMethod returns joining method that must be used with this token. @@ -385,6 +387,11 @@ func (p *ProvisionTokenV2) SetAllowRules(rules []*TokenRule) { p.Spec.Allow = rules } +// GetGCPRules will return the GCP rules within this token. +func (p *ProvisionTokenV2) GetGCPRules() *ProvisionTokenSpecV2GCP { + return p.Spec.GCP +} + // GetAWSIIDTTL returns the TTL of EC2 IIDs func (p *ProvisionTokenV2) GetAWSIIDTTL() Duration { return p.Spec.AWSIIDTTL diff --git a/api/types/types.pb.go b/api/types/types.pb.go index dd2ae679fd9fc..beed0000d2072 100644 --- a/api/types/types.pb.go +++ b/api/types/types.pb.go @@ -4122,7 +4122,7 @@ type ProvisionTokenSpecV2 struct { // to join the cluster with this token. AWSIIDTTL Duration `protobuf:"varint,3,opt,name=AWSIIDTTL,proto3,casttype=Duration" json:"aws_iid_ttl,omitempty"` // JoinMethod is the joining method required in order to use this token. - // Supported joining methods include "token", "ec2", and "iam". + // Supported joining methods include: azure, circleci, ec2, gcp, github, gitlab, iam, kubernetes, spacelift, token, tpm JoinMethod JoinMethod `protobuf:"bytes,4,opt,name=JoinMethod,proto3,casttype=JoinMethod" json:"join_method"` // BotName is the name of the bot this token grants access to, if any BotName string `protobuf:"bytes,5,opt,name=BotName,proto3" json:"bot_name,omitempty"` @@ -11634,7 +11634,10 @@ type OIDCAuthRequest struct { // attestation_statement is an attestation statement for the given public key. AttestationStatement *v1.AttestationStatement `protobuf:"bytes,17,opt,name=attestation_statement,json=attestationStatement,proto3" json:"attestation_statement,omitempty"` // ClientLoginIP specifies IP address of the client for login, it will be written to the user's certificates. - ClientLoginIP string `protobuf:"bytes,18,opt,name=ClientLoginIP,proto3" json:"client_login_ip,omitempty"` + ClientLoginIP string `protobuf:"bytes,18,opt,name=ClientLoginIP,proto3" json:"client_login_ip,omitempty"` + // ClientUserAgent is the user agent of the Web browser, used for issuing a + // DeviceWebToken. + ClientUserAgent string `protobuf:"bytes,19,opt,name=ClientUserAgent,proto3" json:"client_user_agent,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -11883,7 +11886,10 @@ type SAMLAuthRequest struct { // attestation_statement is an attestation statement for the given public key. AttestationStatement *v1.AttestationStatement `protobuf:"bytes,16,opt,name=attestation_statement,json=attestationStatement,proto3" json:"attestation_statement,omitempty"` // ClientLoginIP specifies IP address of the client for login, it will be written to the user's certificates. - ClientLoginIP string `protobuf:"bytes,17,opt,name=ClientLoginIP,proto3" json:"client_login_ip,omitempty"` + ClientLoginIP string `protobuf:"bytes,17,opt,name=ClientLoginIP,proto3" json:"client_login_ip,omitempty"` + // ClientUserAgent is the user agent of the Web browser, used for issuing a + // DeviceWebToken. + ClientUserAgent string `protobuf:"bytes,18,opt,name=ClientUserAgent,proto3" json:"client_user_agent,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -19763,1698 +19769,1703 @@ func init() { func init() { proto.RegisterFile("teleport/legacy/types/types.proto", fileDescriptor_9198ee693835762e) } var fileDescriptor_9198ee693835762e = []byte{ - // 27051 bytes of a gzipped FileDescriptorProto + // 27132 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xbd, 0x6d, 0x70, 0x1c, 0x49, - 0x76, 0x20, 0x36, 0xfd, 0x01, 0xa0, 0xf1, 0xf0, 0xd5, 0x48, 0x80, 0x24, 0x88, 0x19, 0x0e, 0x38, - 0xc5, 0x19, 0x0e, 0x39, 0x1f, 0xe4, 0x12, 0xdc, 0xe1, 0xee, 0xec, 0x7c, 0x36, 0xba, 0x41, 0xa2, - 0x49, 0x10, 0xc0, 0x56, 0x83, 0xc4, 0x8e, 0x66, 0x77, 0x6b, 0x0b, 0xdd, 0x09, 0xa0, 0x06, 0xdd, - 0x5d, 0xbd, 0x55, 0xd5, 0x04, 0xb1, 0xd2, 0x59, 0x2b, 0xe9, 0x74, 0x6b, 0xf9, 0x4e, 0x5f, 0x3e, - 0xe9, 0xa4, 0x73, 0xdc, 0x29, 0x14, 0x3a, 0xdf, 0x59, 0xfe, 0x38, 0x85, 0x43, 0xd2, 0x85, 0xcf, - 0x21, 0x5b, 0x96, 0x1c, 0xb2, 0x42, 0x96, 0x7d, 0x67, 0x45, 0xf8, 0x7b, 0xad, 0x80, 0x2c, 0xeb, - 0xe2, 0xc2, 0xc1, 0x38, 0x3b, 0x74, 0xbe, 0x08, 0x85, 0x3d, 0x0e, 0xd9, 0x8e, 0x7c, 0x99, 0x59, - 0x95, 0x59, 0x55, 0xdd, 0x68, 0x0c, 0x39, 0x27, 0x71, 0xac, 0x3f, 0x24, 0xfa, 0xe5, 0x7b, 0x2f, - 0x3f, 0xeb, 0xe5, 0xcb, 0x97, 0xef, 0xbd, 0x84, 0x17, 0x02, 0xda, 0xa4, 0x1d, 0xd7, 0x0b, 0xae, - 0x36, 0xe9, 0xae, 0x5d, 0x3f, 0xbc, 0x1a, 0x1c, 0x76, 0xa8, 0xcf, 0xff, 0xbd, 0xd2, 0xf1, 0xdc, - 0xc0, 0x25, 0x43, 0xf8, 0x63, 0x7e, 0x76, 0xd7, 0xdd, 0x75, 0x11, 0x72, 0x95, 0xfd, 0xc5, 0x0b, - 0xe7, 0x17, 0x76, 0x5d, 0x77, 0xb7, 0x49, 0xaf, 0xe2, 0xaf, 0xed, 0xee, 0xce, 0xd5, 0xc0, 0x69, - 0x51, 0x3f, 0xb0, 0x5b, 0x1d, 0x81, 0x70, 0x39, 0xac, 0xc0, 0x0e, 0x02, 0x56, 0x12, 0x38, 0x6e, - 0xfb, 0xea, 0x83, 0x6b, 0xea, 0x4f, 0x81, 0xfa, 0x7a, 0x7a, 0x5b, 0x0e, 0x3c, 0xbb, 0xd3, 0xa1, - 0x5e, 0xf4, 0x07, 0x47, 0x37, 0x7e, 0x2e, 0x07, 0xa3, 0x77, 0x28, 0xed, 0x94, 0x9a, 0xce, 0x03, - 0x4a, 0x2e, 0x40, 0x7e, 0xcd, 0x6e, 0xd1, 0xb9, 0xcc, 0xf9, 0xcc, 0xa5, 0xd1, 0xa5, 0xa9, 0x47, - 0x47, 0x0b, 0x63, 0x3e, 0xf5, 0x1e, 0x50, 0xcf, 0x6a, 0xdb, 0x2d, 0x6a, 0x62, 0x21, 0x79, 0x15, - 0x46, 0xd9, 0xff, 0x7e, 0xc7, 0xae, 0xd3, 0xb9, 0x2c, 0x62, 0x4e, 0x3c, 0x3a, 0x5a, 0x18, 0x6d, - 0x4b, 0xa0, 0x19, 0x95, 0x93, 0x2a, 0x8c, 0x2c, 0x3f, 0xec, 0x38, 0x1e, 0xf5, 0xe7, 0xf2, 0xe7, - 0x33, 0x97, 0xc6, 0x16, 0xe7, 0xaf, 0xf0, 0xce, 0x5e, 0x91, 0x9d, 0xbd, 0xb2, 0x29, 0x3b, 0xbb, - 0x34, 0xf3, 0x3b, 0x47, 0x0b, 0xcf, 0x3c, 0x3a, 0x5a, 0x18, 0xa1, 0x9c, 0xe4, 0x27, 0xfe, 0x60, - 0x21, 0x63, 0x4a, 0x7a, 0xf2, 0x36, 0xe4, 0x37, 0x0f, 0x3b, 0x74, 0x6e, 0xf4, 0x7c, 0xe6, 0xd2, - 0xe4, 0xe2, 0xf3, 0x57, 0xf8, 0xf0, 0x86, 0x8d, 0x8f, 0xfe, 0x62, 0x58, 0x4b, 0x85, 0x47, 0x47, - 0x0b, 0x79, 0x86, 0x62, 0x22, 0x15, 0x79, 0x1d, 0x86, 0x57, 0x5c, 0x3f, 0xa8, 0x56, 0xe6, 0x00, - 0x9b, 0x7c, 0xea, 0xd1, 0xd1, 0xc2, 0xf4, 0x9e, 0xeb, 0x07, 0x96, 0xd3, 0x78, 0xcd, 0x6d, 0x39, - 0x01, 0x6d, 0x75, 0x82, 0x43, 0x53, 0x20, 0x19, 0x0f, 0x61, 0x42, 0xe3, 0x47, 0xc6, 0x60, 0xe4, - 0xde, 0xda, 0x9d, 0xb5, 0xf5, 0xad, 0xb5, 0xe2, 0x33, 0xa4, 0x00, 0xf9, 0xb5, 0xf5, 0xca, 0x72, - 0x31, 0x43, 0x46, 0x20, 0x57, 0xda, 0xd8, 0x28, 0x66, 0xc9, 0x38, 0x14, 0x2a, 0xa5, 0xcd, 0xd2, - 0x52, 0xa9, 0xb6, 0x5c, 0xcc, 0x91, 0x19, 0x98, 0xda, 0xaa, 0xae, 0x55, 0xd6, 0xb7, 0x6a, 0x56, - 0x65, 0xb9, 0x76, 0x67, 0x73, 0x7d, 0xa3, 0x98, 0x27, 0x93, 0x00, 0x77, 0xee, 0x2d, 0x2d, 0x9b, - 0x6b, 0xcb, 0x9b, 0xcb, 0xb5, 0xe2, 0x10, 0x99, 0x85, 0xa2, 0x24, 0xb1, 0x6a, 0xcb, 0xe6, 0xfd, - 0x6a, 0x79, 0xb9, 0x38, 0x7c, 0x3b, 0x5f, 0xc8, 0x15, 0xf3, 0xe6, 0xc8, 0x2a, 0xb5, 0x7d, 0x5a, - 0xad, 0x18, 0x7f, 0x27, 0x07, 0x85, 0xbb, 0x34, 0xb0, 0x1b, 0x76, 0x60, 0x93, 0xe7, 0xb4, 0xf9, - 0xc1, 0x2e, 0x2a, 0x13, 0x73, 0x21, 0x39, 0x31, 0x43, 0x8f, 0x8e, 0x16, 0x32, 0xaf, 0xab, 0x13, - 0xf2, 0x16, 0x8c, 0x55, 0xa8, 0x5f, 0xf7, 0x9c, 0x0e, 0x5b, 0x34, 0x73, 0x39, 0x44, 0x3b, 0xfb, - 0xe8, 0x68, 0xe1, 0x54, 0x23, 0x02, 0x2b, 0x03, 0xa2, 0x62, 0x93, 0x2a, 0x0c, 0xaf, 0xda, 0xdb, - 0xb4, 0xe9, 0xcf, 0x0d, 0x9d, 0xcf, 0x5d, 0x1a, 0x5b, 0x7c, 0x56, 0x4c, 0x82, 0x6c, 0xe0, 0x15, - 0x5e, 0xba, 0xdc, 0x0e, 0xbc, 0xc3, 0xa5, 0xd9, 0x47, 0x47, 0x0b, 0xc5, 0x26, 0x02, 0xd4, 0x01, - 0xe6, 0x28, 0xa4, 0x16, 0x2d, 0x8c, 0xe1, 0x63, 0x17, 0xc6, 0xb9, 0xdf, 0x39, 0x5a, 0xc8, 0xb0, - 0x09, 0x13, 0x0b, 0x23, 0xe2, 0xa7, 0x2f, 0x91, 0x45, 0x28, 0x98, 0xf4, 0x81, 0xe3, 0xb3, 0x9e, - 0x15, 0xb0, 0x67, 0xa7, 0x1f, 0x1d, 0x2d, 0x10, 0x4f, 0xc0, 0x94, 0x66, 0x84, 0x78, 0xf3, 0x6f, - 0xc2, 0x98, 0xd2, 0x6a, 0x52, 0x84, 0xdc, 0x3e, 0x3d, 0xe4, 0x23, 0x6c, 0xb2, 0x3f, 0xc9, 0x2c, - 0x0c, 0x3d, 0xb0, 0x9b, 0x5d, 0x31, 0xa4, 0x26, 0xff, 0xf1, 0xa5, 0xec, 0x17, 0x33, 0xb7, 0xf3, - 0x85, 0x91, 0x62, 0xc1, 0xcc, 0x56, 0x2b, 0xc6, 0xbf, 0x9e, 0x87, 0x82, 0xe9, 0xf2, 0x0f, 0x91, - 0x5c, 0x86, 0xa1, 0x5a, 0x60, 0x07, 0x72, 0x9a, 0x66, 0x1e, 0x1d, 0x2d, 0x4c, 0xb1, 0x8f, 0x94, - 0x2a, 0xf5, 0x73, 0x0c, 0x86, 0xba, 0xb1, 0x67, 0xfb, 0x72, 0xba, 0x10, 0xb5, 0xc3, 0x00, 0x2a, - 0x2a, 0x62, 0x90, 0x8b, 0x90, 0xbf, 0xeb, 0x36, 0xa8, 0x98, 0x31, 0xf2, 0xe8, 0x68, 0x61, 0xb2, - 0xe5, 0x36, 0x54, 0x44, 0x2c, 0x27, 0xaf, 0xc1, 0x68, 0xb9, 0xeb, 0x79, 0xb4, 0xcd, 0xd6, 0x7a, - 0x1e, 0x91, 0x27, 0x1f, 0x1d, 0x2d, 0x40, 0x9d, 0x03, 0x2d, 0xa7, 0x61, 0x46, 0x08, 0x6c, 0x1a, - 0x6a, 0x81, 0xed, 0x05, 0xb4, 0x31, 0x37, 0x34, 0xd0, 0x34, 0xb0, 0xef, 0x73, 0xda, 0xe7, 0x24, - 0xf1, 0x69, 0x10, 0x9c, 0xc8, 0x0a, 0x8c, 0xdd, 0xf2, 0xec, 0x3a, 0xdd, 0xa0, 0x9e, 0xe3, 0x36, - 0x70, 0x7e, 0x73, 0x4b, 0x17, 0x1f, 0x1d, 0x2d, 0x9c, 0xde, 0x65, 0x60, 0xab, 0x83, 0xf0, 0x88, - 0xfa, 0xe3, 0xa3, 0x85, 0x42, 0xa5, 0xeb, 0xe1, 0xe8, 0x99, 0x2a, 0x29, 0xf9, 0x06, 0x9b, 0x1c, - 0x3f, 0xc0, 0xa1, 0xa5, 0x8d, 0xb9, 0x91, 0x63, 0x9b, 0x68, 0x88, 0x26, 0x9e, 0x6e, 0xda, 0x7e, - 0x60, 0x79, 0x9c, 0x2e, 0xd6, 0x4e, 0x95, 0x25, 0x59, 0x87, 0x42, 0xad, 0xbe, 0x47, 0x1b, 0xdd, - 0x26, 0xc5, 0x25, 0x33, 0xb6, 0x78, 0x46, 0x2c, 0x6a, 0x39, 0x9f, 0xb2, 0x78, 0x69, 0x5e, 0xf0, - 0x26, 0xbe, 0x80, 0xa8, 0xeb, 0x49, 0x62, 0x7d, 0xa9, 0xf0, 0xb3, 0x3f, 0xbf, 0xf0, 0xcc, 0xb7, - 0x7f, 0xff, 0xfc, 0x33, 0xc6, 0x7f, 0x90, 0x85, 0x62, 0x9c, 0x09, 0xd9, 0x81, 0x89, 0x7b, 0x9d, - 0x86, 0x1d, 0xd0, 0x72, 0xd3, 0xa1, 0xed, 0xc0, 0xc7, 0x45, 0xd2, 0xbf, 0x4f, 0x2f, 0x8a, 0x7a, - 0xe7, 0xba, 0x48, 0x68, 0xd5, 0x39, 0x65, 0xac, 0x57, 0x3a, 0xdb, 0xa8, 0x9e, 0x1a, 0x0a, 0x70, - 0x1f, 0x57, 0xd8, 0xc9, 0xea, 0xe1, 0xa2, 0xbf, 0x47, 0x3d, 0x82, 0xad, 0x58, 0x40, 0xed, 0xc6, - 0xf6, 0x21, 0xae, 0xcc, 0xc1, 0x17, 0x10, 0x23, 0x49, 0x59, 0x40, 0x0c, 0x6c, 0xfc, 0x93, 0x0c, - 0x4c, 0x9a, 0xd4, 0x77, 0xbb, 0x5e, 0x9d, 0xae, 0x50, 0xbb, 0x41, 0x3d, 0xb6, 0xfc, 0xef, 0x38, - 0xed, 0x86, 0xf8, 0xa6, 0x70, 0xf9, 0xef, 0x3b, 0x6d, 0x55, 0x74, 0x63, 0x39, 0xf9, 0x1c, 0x8c, - 0xd4, 0xba, 0xdb, 0x88, 0x9a, 0x8d, 0x24, 0x80, 0xdf, 0xdd, 0xb6, 0x62, 0xe8, 0x12, 0x8d, 0x5c, - 0x85, 0x91, 0xfb, 0xd4, 0xf3, 0x23, 0x69, 0x88, 0x5b, 0xc3, 0x03, 0x0e, 0x52, 0x09, 0x04, 0x16, - 0xb9, 0x15, 0x49, 0x64, 0xb1, 0xa9, 0x4d, 0xc5, 0xe4, 0x60, 0xb4, 0x54, 0x5a, 0x02, 0xa2, 0x2e, - 0x15, 0x89, 0x65, 0xfc, 0x64, 0x16, 0x8a, 0x15, 0x3b, 0xb0, 0xb7, 0x6d, 0x5f, 0x8c, 0xe7, 0xfd, - 0xeb, 0x4c, 0xc6, 0x2b, 0x1d, 0x45, 0x19, 0xcf, 0x5a, 0xfe, 0x89, 0xbb, 0xf7, 0x52, 0xbc, 0x7b, - 0x63, 0x6c, 0x87, 0x15, 0xdd, 0x8b, 0x3a, 0xf5, 0xce, 0xf1, 0x9d, 0x2a, 0x8a, 0x4e, 0x15, 0x64, - 0xa7, 0xa2, 0xae, 0x90, 0x77, 0x20, 0x5f, 0xeb, 0xd0, 0xba, 0x10, 0x22, 0x72, 0x5f, 0xd0, 0x3b, - 0xc7, 0x10, 0xee, 0x5f, 0x5f, 0x1a, 0x17, 0x6c, 0xf2, 0x7e, 0x87, 0xd6, 0x4d, 0x24, 0x53, 0x3e, - 0x9a, 0x7f, 0x98, 0x83, 0xd9, 0x34, 0x32, 0xb5, 0x1f, 0xc3, 0x7d, 0xfa, 0x71, 0x09, 0x0a, 0x6c, - 0x0b, 0x67, 0xdb, 0x22, 0x8a, 0x8b, 0xd1, 0xa5, 0x71, 0xd6, 0xe4, 0x3d, 0x01, 0x33, 0xc3, 0x52, - 0x72, 0x21, 0xd4, 0x08, 0x0a, 0x11, 0x3f, 0xa1, 0x11, 0x48, 0x3d, 0x80, 0xcd, 0xb5, 0xfc, 0x84, - 0x51, 0x71, 0x88, 0x86, 0x45, 0x82, 0xa3, 0xb9, 0xf6, 0x04, 0x44, 0xdb, 0x66, 0xe4, 0xa6, 0xb0, - 0x0c, 0x05, 0xd9, 0xad, 0xb9, 0x71, 0x64, 0x34, 0x1d, 0x1b, 0xa4, 0xfb, 0xd7, 0xf9, 0x64, 0x36, - 0xc4, 0x6f, 0x95, 0x8d, 0xc4, 0x21, 0xd7, 0xa1, 0xb0, 0xe1, 0xb9, 0x0f, 0x0f, 0xab, 0x15, 0x7f, - 0x6e, 0xe2, 0x7c, 0xee, 0xd2, 0xe8, 0xd2, 0x99, 0x47, 0x47, 0x0b, 0x33, 0x1d, 0x06, 0xb3, 0x9c, - 0x86, 0xba, 0xd3, 0x86, 0x88, 0xb7, 0xf3, 0x85, 0x4c, 0x31, 0x7b, 0x3b, 0x5f, 0xc8, 0x16, 0x73, - 0x5c, 0xbd, 0xb8, 0x9d, 0x2f, 0xe4, 0x8b, 0x43, 0xb7, 0xf3, 0x85, 0x21, 0x54, 0x38, 0x46, 0x8b, - 0x70, 0x3b, 0x5f, 0x18, 0x2b, 0x8e, 0x6b, 0xbb, 0x3d, 0x32, 0x08, 0xdc, 0xba, 0xdb, 0x34, 0x73, - 0xf7, 0xcc, 0xaa, 0x39, 0x5c, 0x2e, 0x95, 0xa9, 0x17, 0x98, 0xb9, 0xd2, 0x56, 0xcd, 0x9c, 0xa8, - 0x1c, 0xb6, 0xed, 0x96, 0x53, 0xe7, 0x5b, 0xa7, 0x99, 0xbb, 0x55, 0xde, 0x30, 0x4a, 0x30, 0x19, - 0xf5, 0x65, 0xd5, 0xf1, 0x03, 0x72, 0x15, 0x46, 0x25, 0x84, 0x09, 0xba, 0x5c, 0x6a, 0xaf, 0xcd, - 0x08, 0xc7, 0xf8, 0xed, 0x2c, 0x40, 0x54, 0xf2, 0x94, 0x7e, 0x0b, 0x5f, 0xd0, 0xbe, 0x85, 0x53, - 0xf1, 0x6f, 0xa1, 0xe7, 0x57, 0x40, 0xde, 0x83, 0x61, 0xa6, 0x16, 0x74, 0xa5, 0x4a, 0x74, 0x26, - 0x4e, 0x8a, 0x85, 0xf7, 0xaf, 0x2f, 0x4d, 0x0a, 0xe2, 0x61, 0x1f, 0x21, 0xa6, 0x20, 0x53, 0x3e, - 0xa3, 0x9f, 0x1b, 0x89, 0x26, 0x43, 0x7c, 0x40, 0x97, 0x20, 0x9c, 0x50, 0x31, 0xa0, 0xf8, 0x65, - 0x74, 0xe4, 0x24, 0x87, 0xa5, 0xe4, 0x2c, 0xb0, 0x09, 0x17, 0x83, 0x3a, 0xf2, 0xe8, 0x68, 0x21, - 0xd7, 0xf5, 0x1c, 0x5c, 0x04, 0xe4, 0x2a, 0x88, 0x65, 0x20, 0x06, 0x90, 0xad, 0xbe, 0xe9, 0xba, - 0x6d, 0xd5, 0xa9, 0x17, 0x44, 0x23, 0x3e, 0x97, 0x91, 0xab, 0x85, 0x74, 0x40, 0x5f, 0x2a, 0x73, - 0x79, 0x5c, 0x06, 0x97, 0x52, 0x47, 0xe5, 0x8a, 0x86, 0xca, 0xd5, 0xc8, 0xf3, 0x72, 0x57, 0x6a, - 0xf0, 0x32, 0x2b, 0xa1, 0x52, 0xea, 0x15, 0x90, 0xeb, 0xc0, 0x56, 0xa8, 0x18, 0x7d, 0x10, 0xf5, - 0x94, 0xb6, 0x6a, 0x4b, 0xa7, 0x04, 0xa7, 0x09, 0xfb, 0x40, 0x25, 0x67, 0xd8, 0xe4, 0x2d, 0x60, - 0x4b, 0x58, 0x8c, 0x3b, 0x11, 0x44, 0xb7, 0xca, 0x1b, 0xe5, 0xa6, 0xdb, 0x6d, 0xd4, 0xbe, 0xbc, - 0x1a, 0x11, 0xef, 0xd6, 0x3b, 0x2a, 0xf1, 0xad, 0xf2, 0x06, 0x79, 0x0b, 0x86, 0x4a, 0xdf, 0xea, - 0x7a, 0x54, 0xe8, 0x27, 0xe3, 0xb2, 0x4e, 0x06, 0x5b, 0x3a, 0x23, 0x08, 0xa7, 0x6c, 0xf6, 0x53, - 0xd5, 0xeb, 0xb0, 0x9c, 0xd5, 0xbc, 0xb9, 0x5a, 0x13, 0xba, 0x07, 0x89, 0x0d, 0xcb, 0xe6, 0xaa, - 0xd2, 0xec, 0x40, 0xeb, 0x35, 0xa3, 0x22, 0x57, 0x21, 0x5b, 0xaa, 0xe0, 0x89, 0x68, 0x6c, 0x71, - 0x54, 0x56, 0x5b, 0x59, 0x9a, 0x15, 0x24, 0xe3, 0xb6, 0xfa, 0x19, 0x64, 0x4b, 0x15, 0xb2, 0x04, - 0x43, 0x77, 0x0f, 0x6b, 0x5f, 0x5e, 0x15, 0xc2, 0x6c, 0x46, 0xae, 0x6b, 0x06, 0x5b, 0xc7, 0xcf, - 0xde, 0x8f, 0x5a, 0xdc, 0x3a, 0xf4, 0xbf, 0xd9, 0x54, 0x5b, 0x8c, 0x68, 0x64, 0x03, 0x46, 0x4b, - 0x8d, 0x96, 0xd3, 0xbe, 0xe7, 0x53, 0x6f, 0x6e, 0x0c, 0xf9, 0xcc, 0xc5, 0xda, 0x1d, 0x96, 0x2f, - 0xcd, 0x3d, 0x3a, 0x5a, 0x98, 0xb5, 0xd9, 0x4f, 0xab, 0xeb, 0x53, 0x4f, 0xe1, 0x16, 0x31, 0x21, - 0x1b, 0x00, 0x77, 0xdd, 0xf6, 0xae, 0x5b, 0x0a, 0x9a, 0xb6, 0x1f, 0x13, 0x8f, 0x51, 0x41, 0xa8, - 0x3e, 0x9c, 0x6a, 0x31, 0x98, 0x65, 0x33, 0xa0, 0xc2, 0x50, 0xe1, 0x41, 0x6e, 0xc2, 0xf0, 0xba, - 0x67, 0xd7, 0x9b, 0x74, 0x6e, 0x02, 0xb9, 0xcd, 0x0a, 0x6e, 0x1c, 0x28, 0x7b, 0x3a, 0x27, 0x18, - 0x16, 0x5d, 0x04, 0xab, 0xc7, 0x14, 0x8e, 0x38, 0xbf, 0x05, 0x24, 0xb9, 0x26, 0x53, 0x0e, 0x09, - 0xaf, 0xaa, 0x87, 0x84, 0xe8, 0xa3, 0x2f, 0xbb, 0xad, 0x96, 0xdd, 0x6e, 0x20, 0xed, 0xfd, 0x45, - 0xe5, 0xec, 0x60, 0x7c, 0x13, 0xa6, 0x13, 0x83, 0x75, 0xcc, 0xf9, 0xee, 0x5d, 0x98, 0xaa, 0xd0, - 0x1d, 0xbb, 0xdb, 0x0c, 0xc2, 0x9d, 0x84, 0x7f, 0xa2, 0x78, 0xd2, 0x6a, 0xf0, 0x22, 0x4b, 0x6e, - 0x1f, 0x66, 0x1c, 0xd9, 0x78, 0x07, 0x26, 0xb4, 0xee, 0xb3, 0xa3, 0x42, 0xa9, 0xdb, 0x70, 0x02, - 0x9c, 0xc8, 0x4c, 0x74, 0x54, 0xb0, 0x19, 0x10, 0xa7, 0xcb, 0x8c, 0x10, 0x8c, 0x7f, 0x53, 0xd5, - 0x56, 0x84, 0x24, 0x62, 0xc7, 0x6a, 0x21, 0x0f, 0x32, 0x91, 0xee, 0x94, 0x90, 0x07, 0xa1, 0x34, - 0xb8, 0xcc, 0xbf, 0xcd, 0x6c, 0xe2, 0xdb, 0x1c, 0x13, 0x33, 0x91, 0xb3, 0x0f, 0x7c, 0xfe, 0x45, - 0x86, 0x2b, 0x35, 0xf7, 0xc9, 0x57, 0xea, 0x7b, 0x30, 0x7e, 0xd7, 0x6e, 0xdb, 0xbb, 0xb4, 0xc1, - 0x7a, 0xc0, 0x65, 0xcf, 0xe8, 0xd2, 0xb3, 0x8f, 0x8e, 0x16, 0xce, 0xb4, 0x38, 0x1c, 0x7b, 0xa9, - 0x2e, 0x22, 0x8d, 0x80, 0x5c, 0x93, 0x5f, 0xf6, 0x50, 0xca, 0x97, 0x3d, 0x21, 0x6a, 0x1f, 0xc2, - 0x2f, 0x5b, 0x7c, 0xcf, 0xc6, 0x6f, 0x8c, 0x62, 0x1f, 0xc9, 0x6b, 0x30, 0x6c, 0xd2, 0x5d, 0xb6, - 0xd5, 0x64, 0xa2, 0x49, 0xf2, 0x10, 0xa2, 0x0e, 0x0c, 0xc7, 0x41, 0x3d, 0x83, 0x36, 0xfc, 0x3d, - 0x67, 0x27, 0x10, 0xa3, 0x13, 0xea, 0x19, 0x02, 0xac, 0xe8, 0x19, 0x02, 0xa2, 0x1f, 0x67, 0x39, - 0x8c, 0x49, 0x3f, 0xb3, 0x52, 0x13, 0x83, 0x26, 0x47, 0xd8, 0xac, 0x28, 0x62, 0xc4, 0xd3, 0xb4, - 0x04, 0x86, 0x4d, 0x6e, 0xc0, 0x68, 0xa9, 0x5e, 0x77, 0xbb, 0xca, 0x99, 0x91, 0x7f, 0xb7, 0x1c, - 0xa8, 0x9b, 0x48, 0x22, 0x54, 0x52, 0x83, 0xb1, 0x65, 0x76, 0xd0, 0x72, 0xca, 0x76, 0x7d, 0x4f, - 0x0e, 0x92, 0x94, 0x61, 0x4a, 0x49, 0xf4, 0xe5, 0x52, 0x04, 0xd6, 0x19, 0x50, 0x35, 0x32, 0x28, - 0xb8, 0x64, 0x13, 0xc6, 0x6a, 0xb4, 0xee, 0xd1, 0xa0, 0x16, 0xb8, 0x1e, 0x8d, 0x89, 0x64, 0xa5, - 0x64, 0xe9, 0x79, 0x79, 0xd6, 0xf3, 0x11, 0x68, 0xf9, 0x0c, 0xaa, 0x72, 0x55, 0x90, 0xb9, 0xd2, - 0xde, 0x72, 0xbd, 0xc3, 0xca, 0x92, 0x10, 0xd3, 0xd1, 0x9e, 0xce, 0xc1, 0xaa, 0xd2, 0xce, 0x20, - 0x8d, 0x6d, 0x5d, 0x69, 0xe7, 0x58, 0x38, 0x53, 0x95, 0x1a, 0xea, 0x56, 0x42, 0x68, 0x4f, 0x45, - 0xa3, 0x8c, 0x60, 0x65, 0xa6, 0x1a, 0x3e, 0x6a, 0x66, 0xda, 0x4c, 0x09, 0x2c, 0xd2, 0x01, 0x22, - 0x67, 0x8d, 0x2b, 0xba, 0x4d, 0xea, 0xfb, 0x42, 0x96, 0x9f, 0x8d, 0x4d, 0x7e, 0x84, 0xb0, 0xf4, - 0x92, 0x60, 0x7e, 0x4e, 0x2e, 0x03, 0x71, 0x4e, 0x63, 0x85, 0x4a, 0x3d, 0x29, 0xbc, 0xc9, 0x9b, - 0x00, 0xcb, 0x0f, 0x03, 0xea, 0xb5, 0xed, 0x66, 0x68, 0x07, 0x43, 0xd3, 0x0f, 0x15, 0x50, 0x7d, - 0xa2, 0x15, 0x64, 0x52, 0x86, 0x89, 0x92, 0xef, 0x77, 0x5b, 0xd4, 0x74, 0x9b, 0xb4, 0x64, 0xae, - 0xa1, 0xdc, 0x1f, 0x5d, 0x3a, 0xf7, 0xe8, 0x68, 0xe1, 0xac, 0x8d, 0x05, 0x96, 0xe7, 0x36, 0xa9, - 0x65, 0x7b, 0xea, 0xea, 0xd6, 0x69, 0xc8, 0x3a, 0xc0, 0x7a, 0x87, 0xb6, 0x6b, 0xd4, 0xf6, 0xea, - 0x7b, 0x31, 0x31, 0x1f, 0x15, 0x2c, 0x3d, 0x27, 0x7a, 0x38, 0xeb, 0x76, 0x68, 0xdb, 0x47, 0x98, - 0xda, 0xaa, 0x08, 0x93, 0x6c, 0xc1, 0x54, 0xb5, 0x74, 0x77, 0xc3, 0x6d, 0x3a, 0xf5, 0x43, 0xa1, - 0x39, 0x4d, 0xa2, 0x75, 0xf0, 0xb4, 0xe0, 0x1a, 0x2b, 0xe5, 0xe2, 0xc9, 0xb1, 0x5b, 0x56, 0x07, - 0xa1, 0x96, 0xd0, 0x9f, 0xe2, 0x5c, 0xc8, 0x07, 0x6c, 0x0d, 0xfa, 0x4c, 0x19, 0xdc, 0xb4, 0x77, - 0xfd, 0xb9, 0x29, 0xcd, 0xda, 0x55, 0xda, 0xaa, 0x5d, 0x51, 0x4a, 0xb9, 0x9a, 0x32, 0xcf, 0x17, - 0x22, 0x42, 0xad, 0xc0, 0xde, 0xf5, 0xf5, 0x85, 0x18, 0x62, 0x93, 0xdb, 0x00, 0x15, 0xb7, 0xde, - 0x6d, 0xd1, 0x76, 0x50, 0x59, 0x9a, 0x2b, 0xea, 0x47, 0x81, 0xb0, 0x20, 0x12, 0x6d, 0x0d, 0xb7, - 0xae, 0xad, 0x44, 0x85, 0x7a, 0xfe, 0x5d, 0x28, 0xc6, 0x1b, 0x72, 0x42, 0x03, 0xd6, 0x44, 0x71, - 0x52, 0xe9, 0xfd, 0xf2, 0x43, 0xc7, 0x0f, 0x7c, 0xe3, 0x7b, 0xb5, 0x2f, 0x90, 0x49, 0x87, 0x3b, - 0xf4, 0x70, 0xc3, 0xa3, 0x3b, 0xce, 0x43, 0x21, 0xcc, 0x50, 0x3a, 0xec, 0xd3, 0x43, 0xab, 0x83, - 0x50, 0x55, 0x3a, 0x84, 0xa8, 0xe4, 0xf3, 0x50, 0xb8, 0x73, 0xb7, 0x76, 0x87, 0x1e, 0x56, 0x2b, - 0x62, 0xa3, 0xe2, 0x64, 0x2d, 0xdf, 0x62, 0xa4, 0xda, 0x5a, 0x0b, 0x31, 0x8d, 0xa5, 0x48, 0x12, - 0xb2, 0x9a, 0xcb, 0xcd, 0xae, 0x1f, 0x50, 0xaf, 0x5a, 0x51, 0x6b, 0xae, 0x73, 0x60, 0x4c, 0x2e, - 0x85, 0xa8, 0xc6, 0x3f, 0xcc, 0xa2, 0x14, 0x64, 0x0b, 0xbe, 0xda, 0xf6, 0x03, 0xbb, 0x5d, 0xa7, - 0x21, 0x03, 0x5c, 0xf0, 0x8e, 0x80, 0xc6, 0x16, 0x7c, 0x84, 0xac, 0x57, 0x9d, 0x1d, 0xb8, 0x6a, - 0x56, 0xa5, 0xb4, 0x5c, 0x54, 0x2b, 0xaa, 0x79, 0xd5, 0x13, 0xd0, 0x58, 0x95, 0x11, 0x32, 0xb9, - 0x08, 0x23, 0xd5, 0xd2, 0xdd, 0x52, 0x37, 0xd8, 0x43, 0x19, 0x5c, 0xe0, 0xfa, 0x39, 0x5b, 0xad, - 0x76, 0x37, 0xd8, 0x33, 0x65, 0x21, 0xb9, 0x8a, 0xe7, 0x9e, 0x36, 0x0d, 0xb8, 0x19, 0x56, 0x6c, - 0xba, 0x3e, 0x07, 0xc5, 0x8e, 0x3d, 0x0c, 0x44, 0x5e, 0x81, 0xa1, 0xfb, 0x1b, 0xe5, 0x6a, 0x45, - 0x1c, 0x9c, 0x71, 0x27, 0x7a, 0xd0, 0xa9, 0xeb, 0x2d, 0xe1, 0x28, 0xc6, 0x6f, 0x66, 0x22, 0xf9, - 0x46, 0x2e, 0x6a, 0xfa, 0x08, 0x1a, 0x5d, 0x98, 0x3e, 0xa2, 0x1a, 0x5d, 0x50, 0x33, 0x31, 0x81, - 0x94, 0xbb, 0x7e, 0xe0, 0xb6, 0x96, 0xdb, 0x8d, 0x8e, 0xeb, 0xb4, 0x03, 0xa4, 0xe2, 0xa3, 0x66, - 0x3c, 0x3a, 0x5a, 0x78, 0xbe, 0x8e, 0xa5, 0x16, 0x15, 0xc5, 0x56, 0x8c, 0x4b, 0x0a, 0xf5, 0x63, - 0x0c, 0xa4, 0xf1, 0xbb, 0x59, 0x6d, 0x5f, 0x62, 0xcd, 0x33, 0x69, 0xa7, 0xe9, 0xd4, 0xf1, 0x28, - 0x7e, 0xcb, 0x73, 0xbb, 0x9d, 0x70, 0x39, 0x60, 0xf3, 0xbc, 0xa8, 0xd4, 0xda, 0x65, 0xc5, 0x3a, - 0xef, 0x14, 0x6a, 0xf2, 0x3e, 0x8c, 0x33, 0x15, 0x41, 0xfc, 0xf4, 0xe7, 0xb2, 0x38, 0x13, 0xcf, - 0xa1, 0xf9, 0xcc, 0xa7, 0x5e, 0xc8, 0x46, 0xd3, 0x2d, 0x54, 0x0a, 0xd2, 0x80, 0xb9, 0x4d, 0xcf, - 0x6e, 0xfb, 0x4e, 0xb0, 0xdc, 0xae, 0x7b, 0x87, 0xa8, 0xd2, 0x2c, 0xb7, 0xed, 0xed, 0x26, 0x6d, - 0x60, 0x77, 0x0b, 0x4b, 0x97, 0x1e, 0x1d, 0x2d, 0xbc, 0x18, 0x70, 0x1c, 0x8b, 0x86, 0x48, 0x16, - 0xe5, 0x58, 0x0a, 0xe7, 0x9e, 0x9c, 0x98, 0x0a, 0x24, 0x87, 0x15, 0x6f, 0x4f, 0xf8, 0xee, 0x8e, - 0x2a, 0x50, 0x38, 0x1b, 0x4c, 0xf8, 0xa8, 0xcd, 0x54, 0x09, 0x8c, 0x3f, 0xc9, 0x44, 0x3b, 0x27, - 0x79, 0x1b, 0xc6, 0xc4, 0x52, 0x57, 0xd6, 0x05, 0x8a, 0x3e, 0xf9, 0x5d, 0xc4, 0x66, 0x56, 0x45, - 0x67, 0x07, 0xf6, 0x52, 0x79, 0x55, 0x59, 0x1b, 0x78, 0x60, 0xb7, 0xeb, 0xcd, 0x38, 0x95, 0x44, - 0x63, 0x8b, 0x60, 0x73, 0xb5, 0xa6, 0x8f, 0x0a, 0x2e, 0x82, 0xa0, 0xe9, 0xa7, 0x0c, 0x83, 0x82, - 0xfc, 0xf8, 0x1d, 0xff, 0xef, 0x33, 0x69, 0x1b, 0x34, 0x59, 0x82, 0x89, 0x2d, 0xd7, 0xdb, 0xc7, - 0xf9, 0x55, 0x06, 0x01, 0x67, 0xfe, 0x40, 0x16, 0xc4, 0x3b, 0xa4, 0x93, 0xa8, 0x6d, 0x53, 0x46, - 0x43, 0x6f, 0x5b, 0x8c, 0x83, 0x46, 0xc0, 0xe6, 0x21, 0xe4, 0x18, 0x7e, 0x1d, 0x38, 0x0f, 0x51, - 0x13, 0xb4, 0x25, 0xac, 0xa2, 0x1b, 0xff, 0x71, 0x46, 0xdd, 0x88, 0xd9, 0x20, 0x57, 0xdc, 0x96, - 0xed, 0xb4, 0x95, 0xee, 0xf0, 0x1b, 0x21, 0x84, 0xc6, 0x5b, 0xa2, 0x20, 0x93, 0xeb, 0x50, 0xe0, - 0xbf, 0x42, 0x21, 0x89, 0xe6, 0x28, 0x41, 0xa8, 0x4b, 0x78, 0x89, 0x98, 0x98, 0x99, 0xdc, 0x49, - 0x67, 0xe6, 0x37, 0x32, 0xea, 0x1e, 0xfa, 0x49, 0x77, 0x89, 0xd8, 0xee, 0x90, 0x3d, 0xc9, 0xee, - 0xf0, 0xd8, 0x5d, 0xf8, 0x76, 0x06, 0xc6, 0x14, 0xf3, 0x02, 0xeb, 0xc3, 0x86, 0xe7, 0x7e, 0x44, - 0xeb, 0x81, 0xde, 0x87, 0x0e, 0x07, 0xc6, 0xfa, 0x10, 0xa2, 0x3e, 0x46, 0x1f, 0x8c, 0x7f, 0x9e, - 0x11, 0x87, 0x9b, 0x81, 0xc5, 0xbc, 0x2e, 0x92, 0xb3, 0x27, 0xd9, 0xdb, 0xde, 0x87, 0x21, 0x93, - 0x36, 0x1c, 0x5f, 0x1c, 0x4c, 0xa6, 0xd5, 0x83, 0x14, 0x16, 0x44, 0x0a, 0x8f, 0xc7, 0x7e, 0xaa, - 0x1b, 0x13, 0x96, 0x33, 0x0d, 0xb4, 0xea, 0xdf, 0x6c, 0xd2, 0x87, 0x0e, 0xff, 0x18, 0xc5, 0x1e, - 0x89, 0x1a, 0xa8, 0xe3, 0x5b, 0x3b, 0xac, 0x44, 0xa8, 0xc2, 0xea, 0x87, 0xa7, 0xd1, 0x18, 0x1f, - 0x00, 0x44, 0x55, 0x92, 0x3b, 0x50, 0x14, 0xab, 0xc1, 0x69, 0xef, 0x72, 0x0d, 0x48, 0x8c, 0xc1, - 0xc2, 0xa3, 0xa3, 0x85, 0x67, 0xeb, 0x61, 0x99, 0x50, 0x17, 0x15, 0xbe, 0x09, 0x42, 0xe3, 0xdf, - 0xca, 0x42, 0xb6, 0x84, 0x13, 0x72, 0x87, 0x1e, 0x06, 0xf6, 0xf6, 0x4d, 0xa7, 0xa9, 0x7d, 0x4c, - 0xfb, 0x08, 0xb5, 0x76, 0x1c, 0xcd, 0xce, 0xa0, 0x20, 0xb3, 0x8f, 0xe9, 0x8e, 0xb7, 0xfd, 0x06, - 0x12, 0x2a, 0x1f, 0xd3, 0xbe, 0xb7, 0xfd, 0x46, 0x9c, 0x2c, 0x44, 0x24, 0x06, 0x0c, 0xf3, 0x0f, - 0x4b, 0xac, 0x41, 0x78, 0x74, 0xb4, 0x30, 0xcc, 0xbf, 0x3f, 0x53, 0x94, 0x90, 0xb3, 0x90, 0xab, - 0x6d, 0xac, 0x09, 0x09, 0x88, 0xf6, 0x3c, 0xbf, 0xd3, 0x36, 0x19, 0x8c, 0xd5, 0xb9, 0x5a, 0x29, - 0x6d, 0xe0, 0x09, 0x7e, 0x28, 0xaa, 0xb3, 0xd9, 0xb0, 0x3b, 0xf1, 0x33, 0x7c, 0x88, 0x48, 0xde, - 0x81, 0xb1, 0x3b, 0x95, 0xf2, 0x8a, 0xeb, 0x73, 0xe9, 0x35, 0x1c, 0x2d, 0xfe, 0xfd, 0x46, 0xdd, - 0x42, 0x13, 0x7a, 0x7c, 0x1b, 0x50, 0xf0, 0x8d, 0x1f, 0xce, 0xc2, 0x98, 0x62, 0xe0, 0x22, 0x9f, - 0x17, 0x37, 0x9b, 0x19, 0x4d, 0x75, 0x57, 0x30, 0x58, 0x29, 0xb7, 0x86, 0xb4, 0xdc, 0x06, 0x15, - 0xf7, 0x9c, 0x91, 0xe5, 0x21, 0x3b, 0x88, 0xe5, 0xe1, 0x4d, 0x00, 0xbe, 0x06, 0xb0, 0xc9, 0x8a, - 0x3a, 0xa1, 0x38, 0x38, 0xa8, 0xf3, 0x12, 0x21, 0x93, 0xfb, 0x30, 0xb3, 0xe9, 0x75, 0xfd, 0xa0, - 0x76, 0xe8, 0x07, 0xb4, 0xc5, 0xb8, 0x6d, 0xb8, 0x6e, 0x53, 0xac, 0xbf, 0x17, 0x1f, 0x1d, 0x2d, - 0x9c, 0x0f, 0x58, 0xb1, 0xe5, 0x63, 0x39, 0x36, 0xc0, 0xea, 0xb8, 0xae, 0x6a, 0x8f, 0x48, 0x63, - 0x60, 0x98, 0x30, 0xae, 0x5a, 0x33, 0xd8, 0xce, 0x22, 0x6e, 0x81, 0x84, 0x8d, 0x5a, 0xd9, 0x59, - 0x44, 0x2b, 0x93, 0xb7, 0x52, 0x3a, 0x89, 0xf1, 0x79, 0xd5, 0x92, 0x36, 0xe8, 0x87, 0x6d, 0xfc, - 0x60, 0x26, 0x12, 0x23, 0xf7, 0xaf, 0x91, 0xb7, 0x60, 0x98, 0xdf, 0xba, 0x89, 0xcb, 0xc9, 0x53, - 0xe1, 0x69, 0x54, 0xbd, 0x92, 0xe3, 0x26, 0xec, 0xdf, 0xe3, 0x37, 0xf3, 0xcf, 0x98, 0x82, 0x24, - 0xb4, 0x7e, 0xeb, 0x86, 0x30, 0xc9, 0x1d, 0xed, 0xbc, 0xd7, 0xd2, 0xac, 0xdf, 0xc6, 0x6f, 0xe5, - 0x61, 0x52, 0x47, 0x53, 0xaf, 0xe6, 0x32, 0x03, 0x5d, 0xcd, 0xbd, 0x0f, 0x05, 0x36, 0x1e, 0x4e, - 0x9d, 0x4a, 0x8d, 0xec, 0x45, 0xbc, 0x13, 0x10, 0x30, 0xed, 0xca, 0x19, 0xf8, 0x74, 0xb0, 0xc3, - 0xa9, 0x19, 0x52, 0x91, 0x45, 0xe5, 0xfe, 0x28, 0x17, 0x29, 0x29, 0xf2, 0xfe, 0x48, 0xfd, 0x1e, - 0xc2, 0x9b, 0xa4, 0xd7, 0x61, 0x98, 0x29, 0xe6, 0xa1, 0xed, 0x04, 0x5b, 0xc9, 0x74, 0xf6, 0x98, - 0x6f, 0x09, 0x47, 0x22, 0x5b, 0x50, 0x58, 0xb5, 0xfd, 0xa0, 0x46, 0x69, 0x7b, 0x80, 0x4b, 0xf7, - 0x05, 0x31, 0x54, 0x33, 0x78, 0xa3, 0xed, 0x53, 0xda, 0x8e, 0xdd, 0x9a, 0x86, 0xcc, 0xc8, 0xd7, - 0x00, 0xca, 0x6e, 0x3b, 0xf0, 0xdc, 0xe6, 0xaa, 0xbb, 0x3b, 0x37, 0x8c, 0x87, 0xd6, 0xe7, 0x63, - 0x13, 0x10, 0x21, 0xf0, 0x73, 0x6b, 0x68, 0x99, 0xa9, 0xf3, 0x02, 0xab, 0xe9, 0xee, 0xaa, 0xdf, - 0x41, 0x84, 0x4f, 0x6e, 0x42, 0x51, 0x5a, 0x04, 0xee, 0x75, 0x76, 0x3d, 0x5c, 0x20, 0x23, 0x91, - 0xe6, 0x41, 0x1f, 0x06, 0x56, 0x57, 0xc0, 0x55, 0x49, 0x19, 0xa7, 0x21, 0x5f, 0x85, 0x33, 0x71, - 0x98, 0x9c, 0xe5, 0x42, 0xa4, 0x93, 0xab, 0xec, 0x52, 0xd6, 0x7d, 0x2f, 0x16, 0xc6, 0xc7, 0x59, - 0x38, 0xd3, 0xa3, 0xb3, 0xec, 0x7b, 0xc0, 0xed, 0x5a, 0xf9, 0x1e, 0x62, 0xbb, 0x34, 0x77, 0x16, - 0x3a, 0x0f, 0x59, 0xb1, 0xc1, 0xe5, 0x97, 0x8a, 0x8f, 0x8e, 0x16, 0xc6, 0xb5, 0x79, 0xcc, 0x56, - 0x2b, 0xe4, 0x36, 0xe4, 0xd9, 0x14, 0x0d, 0x70, 0xe7, 0x2d, 0x8d, 0x41, 0x93, 0x81, 0xa3, 0x2e, - 0x1f, 0x9c, 0x3a, 0xe4, 0x41, 0x3e, 0x0f, 0xb9, 0xcd, 0xcd, 0x55, 0x5c, 0x3b, 0x39, 0xec, 0xfb, - 0x44, 0x10, 0x34, 0xb5, 0xa5, 0x3a, 0xc1, 0x68, 0xaf, 0x84, 0x2e, 0x12, 0x0c, 0x9d, 0x7c, 0x25, - 0xe6, 0x8b, 0xf3, 0x4a, 0xff, 0x89, 0x1e, 0xdc, 0x35, 0xe7, 0x31, 0x3c, 0x62, 0x8c, 0xbf, 0x95, - 0x8d, 0xbe, 0xe1, 0x9b, 0x4e, 0x33, 0xa0, 0x1e, 0x99, 0xe7, 0x9f, 0x64, 0xa4, 0x9c, 0x99, 0xe1, - 0x6f, 0x32, 0x17, 0x7d, 0xdf, 0x9c, 0x55, 0xf8, 0x21, 0xbf, 0xa2, 0x7c, 0xc8, 0x39, 0xfc, 0x90, - 0x27, 0x7b, 0x7e, 0xb2, 0xaf, 0xa4, 0xac, 0x4b, 0xfc, 0x10, 0x53, 0xd6, 0xde, 0x8b, 0x30, 0xb1, - 0xe6, 0x2e, 0x3f, 0x0c, 0x42, 0x44, 0xf6, 0x01, 0x16, 0x4c, 0x1d, 0xc8, 0x38, 0xae, 0x37, 0x1b, - 0xd4, 0xdb, 0xdc, 0xb3, 0xdb, 0xda, 0xa5, 0xb3, 0x99, 0x80, 0x33, 0xdc, 0x35, 0x7a, 0xa0, 0xe3, - 0x8e, 0x70, 0xdc, 0x38, 0xdc, 0xf8, 0x81, 0xac, 0x1c, 0x8c, 0xfb, 0x8b, 0x4f, 0xe9, 0xe5, 0xe6, - 0x1b, 0xda, 0xe5, 0xe6, 0x4c, 0x68, 0x96, 0x0d, 0x6f, 0xea, 0x17, 0x8f, 0xb9, 0xe0, 0xff, 0x1f, - 0x86, 0x60, 0x5c, 0x45, 0x67, 0xe3, 0x50, 0x6a, 0x34, 0x3c, 0x75, 0x1c, 0xec, 0x46, 0xc3, 0x33, - 0x11, 0xaa, 0xdd, 0xe7, 0xe7, 0xfa, 0xde, 0xe7, 0x7f, 0x1d, 0x46, 0xcb, 0xad, 0x86, 0x76, 0xcb, - 0x68, 0xa4, 0x34, 0xef, 0x4a, 0x88, 0xc4, 0xbf, 0x85, 0xd0, 0xda, 0x58, 0x6f, 0x35, 0x92, 0x77, - 0x8b, 0x11, 0x4b, 0xcd, 0x15, 0x60, 0xe8, 0x71, 0x5c, 0x01, 0x6e, 0xc0, 0xe8, 0x3d, 0x9f, 0x6e, - 0x76, 0xdb, 0x6d, 0xda, 0xc4, 0x65, 0x55, 0xe0, 0xba, 0x7e, 0xd7, 0xa7, 0x56, 0x80, 0x50, 0xb5, - 0x01, 0x21, 0xaa, 0x3a, 0xc1, 0x23, 0x7d, 0x26, 0xf8, 0x3a, 0x14, 0x36, 0x28, 0xf5, 0x70, 0x4c, - 0xc7, 0x22, 0x95, 0xae, 0x43, 0xa9, 0x67, 0xb1, 0x81, 0xd5, 0x5c, 0x04, 0x04, 0xa2, 0xe6, 0x57, - 0x30, 0x3e, 0xa0, 0x5f, 0x01, 0x79, 0x01, 0xc6, 0x3b, 0xdd, 0xed, 0xa6, 0x53, 0x47, 0xbe, 0xc2, - 0x21, 0xc1, 0x1c, 0xe3, 0x30, 0xc6, 0xd6, 0x27, 0x5f, 0x81, 0x09, 0x3c, 0xe3, 0x84, 0x4b, 0x6e, - 0x52, 0xbb, 0x8e, 0xd3, 0xca, 0xb8, 0xa6, 0x53, 0x67, 0x20, 0x2b, 0xc5, 0x6f, 0x46, 0x67, 0x34, - 0x5f, 0x83, 0x49, 0x7d, 0x26, 0x9f, 0xc0, 0xad, 0x5c, 0xe8, 0x23, 0x51, 0x28, 0x8e, 0xde, 0xce, - 0x17, 0xa0, 0x38, 0xc6, 0xbd, 0x23, 0x4c, 0xd8, 0x08, 0xfb, 0x64, 0x92, 0x3b, 0xdd, 0x6d, 0xea, - 0xb5, 0x69, 0x40, 0x7d, 0x71, 0x08, 0xf0, 0xcd, 0x7c, 0xa9, 0xd3, 0xf1, 0x8d, 0x5f, 0xcd, 0xc2, - 0x48, 0x69, 0xab, 0x56, 0x6d, 0xef, 0xb8, 0x78, 0xb7, 0x16, 0x5e, 0xa9, 0xa8, 0x77, 0x6b, 0xe1, - 0x95, 0x8a, 0x7a, 0x91, 0x72, 0x35, 0xe5, 0x18, 0x87, 0xee, 0xb7, 0xca, 0x31, 0x4e, 0x3b, 0x80, - 0x46, 0xb7, 0x4b, 0xb9, 0x01, 0x6e, 0x97, 0x42, 0x03, 0x60, 0xfe, 0x58, 0x03, 0x20, 0x79, 0x0b, - 0xc6, 0xaa, 0xed, 0x80, 0xee, 0x7a, 0xd1, 0x4a, 0x0f, 0x8f, 0x94, 0x21, 0x58, 0x55, 0xed, 0x15, - 0x6c, 0xb6, 0x8c, 0xb8, 0xd1, 0x31, 0x34, 0x36, 0xe2, 0x32, 0xe2, 0xb6, 0xc9, 0x98, 0x3d, 0x40, - 0x22, 0x1a, 0x95, 0xd8, 0x1a, 0x91, 0x37, 0xf8, 0x5c, 0xf9, 0x9c, 0x8c, 0xac, 0xee, 0x6c, 0x60, - 0x97, 0xa6, 0xd3, 0x6f, 0xf0, 0x8d, 0xef, 0x64, 0x61, 0xac, 0xd4, 0xe9, 0x3c, 0xe5, 0x7e, 0x54, - 0x5f, 0xd4, 0xc4, 0xab, 0x3c, 0x0b, 0x85, 0xfd, 0x1a, 0xc8, 0x85, 0xea, 0x97, 0xb2, 0x30, 0x15, - 0xa3, 0x50, 0x5b, 0x9f, 0x19, 0xd0, 0x7b, 0x2a, 0x3b, 0xa0, 0xf7, 0x54, 0x6e, 0x30, 0xef, 0xa9, - 0xfc, 0xe3, 0x88, 0xcc, 0x97, 0x21, 0x57, 0xea, 0x74, 0xe2, 0xb7, 0xb0, 0x9d, 0xce, 0xfd, 0xeb, - 0xfc, 0x3c, 0x6b, 0x77, 0x3a, 0x26, 0xc3, 0xd0, 0xe4, 0xd8, 0xf0, 0x80, 0x72, 0xcc, 0x78, 0x1d, - 0x46, 0x91, 0x17, 0xfa, 0x2c, 0x9d, 0x07, 0xfc, 0x98, 0x85, 0xbb, 0x92, 0x56, 0x97, 0xf8, 0xcc, - 0xff, 0xef, 0x0c, 0x0c, 0xe1, 0xef, 0xa7, 0x74, 0x8d, 0x2d, 0x6a, 0x6b, 0xac, 0xa8, 0xac, 0xb1, - 0x41, 0x56, 0xd7, 0xff, 0x9e, 0xc7, 0xd1, 0x12, 0xeb, 0x4a, 0xb8, 0x0a, 0x65, 0x52, 0x5c, 0x85, - 0xde, 0x04, 0x45, 0x6a, 0xaa, 0xd6, 0x22, 0x65, 0xcf, 0x50, 0x4f, 0x1a, 0x11, 0x32, 0xd9, 0x8f, - 0x3b, 0x0d, 0xe5, 0x70, 0x32, 0x2e, 0xc4, 0x9b, 0xfa, 0x44, 0xfc, 0x85, 0x56, 0x80, 0x54, 0xdb, - 0x3e, 0xad, 0x77, 0x3d, 0x5a, 0xdb, 0x77, 0x3a, 0xf7, 0xa9, 0xe7, 0xec, 0x1c, 0x8a, 0xd3, 0x3d, - 0xee, 0xcb, 0x8e, 0x28, 0xb5, 0xfc, 0x7d, 0xa7, 0xc3, 0x8e, 0x22, 0xce, 0xce, 0xa1, 0x99, 0x42, - 0x43, 0xde, 0x83, 0x11, 0x93, 0x1e, 0x78, 0x4e, 0x20, 0xaf, 0xc2, 0x27, 0xc3, 0x83, 0x33, 0x42, - 0xf9, 0xc1, 0xd0, 0xe3, 0x3f, 0xd4, 0xf9, 0x17, 0xe5, 0x64, 0x91, 0x0b, 0x3e, 0x7e, 0xe5, 0x3d, - 0x11, 0xf5, 0xb6, 0xb4, 0x55, 0xeb, 0x25, 0xf7, 0xc8, 0x65, 0x18, 0x42, 0xe9, 0x29, 0x74, 0x02, - 0x74, 0x21, 0xc7, 0x3d, 0x54, 0x15, 0xed, 0x88, 0x41, 0x9e, 0x07, 0x08, 0x6f, 0x20, 0xfc, 0xb9, - 0x02, 0xee, 0xd6, 0x0a, 0x24, 0x2e, 0xfa, 0x47, 0x4f, 0x22, 0xfa, 0x3f, 0x3d, 0x4f, 0x99, 0x5f, - 0xca, 0xc2, 0x85, 0x50, 0x9c, 0xad, 0x7b, 0xb5, 0xd2, 0xdd, 0xd5, 0x6a, 0x63, 0x43, 0x68, 0xff, - 0x1b, 0x9e, 0xfb, 0xc0, 0x61, 0xa7, 0xbf, 0x6b, 0xc7, 0x7c, 0x8c, 0xab, 0x7c, 0xd5, 0x72, 0xd3, - 0x61, 0x56, 0xf3, 0x29, 0x50, 0x76, 0x0d, 0xe1, 0xf6, 0xd0, 0xe9, 0x24, 0x2c, 0x89, 0x2b, 0xcf, - 0x98, 0x11, 0x03, 0xf2, 0x83, 0x19, 0x38, 0x9d, 0xde, 0x10, 0x71, 0x22, 0x5c, 0x90, 0x9a, 0x67, - 0x8f, 0xd6, 0x2e, 0xbd, 0xfc, 0xe8, 0x68, 0xe1, 0x82, 0x6f, 0xb7, 0x9a, 0x96, 0xd3, 0xe0, 0xb5, - 0x39, 0x75, 0x6a, 0x75, 0x04, 0x82, 0x56, 0x6f, 0x8f, 0x9a, 0xbe, 0x04, 0xf2, 0x9b, 0x9c, 0xcb, - 0x2c, 0x01, 0x14, 0xa4, 0x75, 0xc6, 0xf8, 0x07, 0x19, 0x50, 0x56, 0x54, 0xc1, 0xa4, 0x0d, 0xc7, - 0xa3, 0xf5, 0x00, 0x25, 0x5a, 0x18, 0x11, 0xc1, 0x61, 0x31, 0x17, 0x12, 0x84, 0x91, 0x77, 0x61, - 0x84, 0xdb, 0x72, 0xb8, 0x0d, 0x25, 0x5a, 0x89, 0xc2, 0xee, 0xc3, 0x43, 0x67, 0x38, 0x86, 0xba, - 0x8a, 0x05, 0x11, 0xd3, 0x6f, 0x6f, 0x6f, 0x6d, 0x96, 0x9b, 0xb6, 0xd3, 0xf2, 0x85, 0x1c, 0xc3, - 0x61, 0xfd, 0xe8, 0x20, 0xb0, 0xea, 0x08, 0x55, 0xf5, 0xdb, 0x10, 0xd5, 0xb8, 0x25, 0xcd, 0x4e, - 0xc7, 0xf8, 0x41, 0x2d, 0xc0, 0xd0, 0xfd, 0xe8, 0xf8, 0xb9, 0x34, 0xfa, 0xe8, 0x68, 0x81, 0x2f, - 0x17, 0x93, 0xc3, 0x8d, 0xbf, 0x9a, 0x81, 0x49, 0x7d, 0x3d, 0x91, 0x2b, 0x30, 0x2c, 0xa2, 0x11, - 0x32, 0x78, 0xcc, 0x66, 0xa3, 0x30, 0xcc, 0xe3, 0x10, 0xb4, 0xe8, 0x03, 0x81, 0xc5, 0x24, 0xb1, - 0xe0, 0x20, 0xec, 0x48, 0x28, 0x89, 0xeb, 0x1c, 0x64, 0xca, 0x32, 0x62, 0x30, 0x35, 0xcc, 0xef, - 0x36, 0x03, 0xd5, 0xfa, 0xea, 0x21, 0xc4, 0x14, 0x25, 0x46, 0x19, 0x86, 0xf9, 0x27, 0x1c, 0xf3, - 0xbf, 0xc8, 0x9c, 0xc0, 0xff, 0xc2, 0x38, 0xca, 0x00, 0xd4, 0x6a, 0x2b, 0x77, 0xe8, 0xe1, 0x86, - 0xed, 0x78, 0x78, 0x5d, 0x80, 0xe2, 0xf2, 0x8e, 0xf8, 0xb8, 0xc6, 0xc5, 0x75, 0x01, 0x17, 0xad, - 0xfb, 0xf4, 0x50, 0xbb, 0x2e, 0x90, 0xa8, 0x28, 0x93, 0x3d, 0xe7, 0x81, 0x1d, 0x50, 0x46, 0x98, - 0x45, 0x42, 0x2e, 0x93, 0x39, 0x34, 0x46, 0xa9, 0x20, 0x93, 0xaf, 0xc1, 0x64, 0xf4, 0x2b, 0xbc, - 0xf4, 0x98, 0x0c, 0x3f, 0x60, 0xbd, 0x70, 0xe9, 0xf9, 0x47, 0x47, 0x0b, 0xf3, 0x0a, 0xd7, 0xf8, - 0x75, 0x48, 0x8c, 0x99, 0xf1, 0x0b, 0x19, 0xbc, 0xea, 0x93, 0x1d, 0xbc, 0x08, 0xf9, 0xd0, 0xab, - 0x6c, 0x9c, 0x5b, 0x6a, 0x62, 0x86, 0x5d, 0x2c, 0x27, 0x17, 0x20, 0x17, 0xf5, 0x04, 0x45, 0xa4, - 0xde, 0x03, 0x56, 0x4a, 0x6e, 0xc1, 0xc8, 0x40, 0x6d, 0xc6, 0x4f, 0x23, 0xa5, 0xad, 0x92, 0x1a, - 0x67, 0xe1, 0xf6, 0xd6, 0xe6, 0x67, 0x77, 0x16, 0x7e, 0x3c, 0x0b, 0x53, 0x6c, 0x5c, 0x4b, 0xdd, - 0x60, 0xcf, 0xf5, 0x9c, 0xe0, 0xf0, 0xa9, 0xb5, 0x53, 0xbc, 0xad, 0x29, 0x39, 0xf3, 0x72, 0x97, - 0x51, 0xfb, 0x36, 0x90, 0xb9, 0xe2, 0xbf, 0x1c, 0x82, 0x99, 0x14, 0x2a, 0xf2, 0x9a, 0x66, 0x4a, - 0x9c, 0x93, 0xd1, 0x86, 0x1f, 0x1f, 0x2d, 0x8c, 0x4b, 0xf4, 0xcd, 0x28, 0xfa, 0x70, 0x51, 0xbf, - 0x37, 0xe7, 0x23, 0x85, 0x96, 0x45, 0xf5, 0xde, 0x5c, 0xbf, 0x2d, 0xbf, 0x0c, 0x43, 0xa6, 0xdb, - 0xa4, 0xd2, 0xc9, 0x03, 0x37, 0x76, 0x8f, 0x01, 0xb4, 0xbb, 0x31, 0x06, 0x20, 0x2b, 0x30, 0xc2, - 0xfe, 0xb8, 0x6b, 0x77, 0x84, 0xd5, 0x97, 0x84, 0x6a, 0x36, 0x42, 0x3b, 0x4e, 0x7b, 0x57, 0xd5, - 0xb4, 0x9b, 0xd4, 0x6a, 0xd9, 0x1d, 0x4d, 0x03, 0xe1, 0x88, 0x9a, 0xc6, 0x5e, 0xe8, 0xad, 0xb1, - 0x67, 0x8e, 0xd5, 0xd8, 0x77, 0x00, 0x6a, 0xce, 0x6e, 0xdb, 0x69, 0xef, 0x96, 0x9a, 0xbb, 0x22, - 0x66, 0xf3, 0x72, 0xef, 0x59, 0xb8, 0x12, 0x21, 0xe3, 0xc2, 0x7d, 0x16, 0xaf, 0x66, 0x38, 0xcc, - 0xb2, 0x9b, 0xbb, 0x9a, 0x6f, 0xb9, 0xc2, 0x99, 0xac, 0x01, 0x94, 0xea, 0x81, 0xf3, 0x80, 0x2d, - 0x61, 0x5f, 0x78, 0x23, 0xcb, 0x26, 0x97, 0x4b, 0x77, 0xe8, 0x61, 0x8d, 0x06, 0x91, 0x91, 0xdb, - 0x46, 0x54, 0xf6, 0x25, 0x68, 0x8e, 0xc3, 0x11, 0x07, 0xd2, 0x81, 0x53, 0xa5, 0x46, 0xc3, 0x61, - 0x7d, 0xb0, 0x9b, 0x78, 0x6b, 0x43, 0x1b, 0xc8, 0x7a, 0x3c, 0x9d, 0xf5, 0x65, 0xc1, 0xfa, 0x05, - 0x3b, 0xa4, 0xb2, 0x02, 0x4e, 0x16, 0xaf, 0x26, 0x9d, 0xb1, 0x51, 0x83, 0x49, 0xbd, 0xf3, 0x7a, - 0xac, 0xe9, 0x38, 0x14, 0xcc, 0x5a, 0xc9, 0xaa, 0xad, 0x94, 0xae, 0x15, 0x33, 0xa4, 0x08, 0xe3, - 0xe2, 0xd7, 0xa2, 0xb5, 0xf8, 0xc6, 0x8d, 0x62, 0x56, 0x83, 0xbc, 0x71, 0x6d, 0xb1, 0x98, 0x9b, - 0xcf, 0xce, 0x65, 0x62, 0x61, 0x1e, 0x23, 0xc5, 0x02, 0x37, 0x67, 0x18, 0xbf, 0x9c, 0x81, 0x82, - 0x6c, 0x3b, 0xb9, 0x01, 0xb9, 0x5a, 0x6d, 0x25, 0x16, 0x98, 0x11, 0xed, 0x32, 0x5c, 0x9e, 0xfa, - 0xbe, 0xea, 0x7d, 0xc7, 0x08, 0x18, 0xdd, 0xe6, 0x6a, 0x4d, 0x28, 0x07, 0x92, 0x2e, 0x12, 0xde, - 0x9c, 0x2e, 0xc5, 0x5b, 0xfd, 0x06, 0xe4, 0x6e, 0x6f, 0x6d, 0x0a, 0x65, 0x5e, 0xd2, 0x45, 0xf2, - 0x94, 0xd3, 0x7d, 0x74, 0xa0, 0x4a, 0x79, 0x46, 0x60, 0x98, 0x30, 0xa6, 0x2c, 0x64, 0xbe, 0xe9, - 0xb6, 0xdc, 0x30, 0xc0, 0x52, 0x6c, 0xba, 0x0c, 0x62, 0x8a, 0x12, 0xa6, 0x23, 0xac, 0xba, 0x75, - 0xbb, 0x29, 0x76, 0x6f, 0xd4, 0x11, 0x9a, 0x0c, 0x60, 0x72, 0xb8, 0xf1, 0x9b, 0x19, 0x28, 0xa2, - 0x26, 0x85, 0xde, 0x73, 0xee, 0x3e, 0x6d, 0xdf, 0xbf, 0x46, 0x5e, 0x97, 0x9f, 0x5c, 0x26, 0x3c, - 0x3a, 0x0e, 0xe1, 0x27, 0x17, 0xb3, 0x3d, 0x8b, 0xcf, 0x4e, 0x89, 0x61, 0xcd, 0x0e, 0x1e, 0xfb, - 0x76, 0x4c, 0x0c, 0xeb, 0x02, 0x0c, 0x61, 0x73, 0x84, 0x70, 0xc4, 0x96, 0x07, 0x0c, 0x60, 0x72, - 0xb8, 0x22, 0x9b, 0x7e, 0x32, 0x9b, 0xe8, 0xc3, 0xe2, 0x67, 0x2a, 0x7e, 0x4c, 0xef, 0xdc, 0x40, - 0xf2, 0xfa, 0x03, 0x98, 0x8d, 0x0f, 0x09, 0x1e, 0xeb, 0x4b, 0x30, 0xa5, 0xc3, 0xe5, 0x09, 0xff, - 0x4c, 0x6a, 0x5d, 0xf7, 0x17, 0xcd, 0x38, 0xbe, 0xf1, 0xbf, 0x64, 0x60, 0x14, 0xff, 0x34, 0xbb, - 0x4d, 0x74, 0x86, 0x28, 0x6d, 0xd5, 0x84, 0x09, 0x4f, 0x55, 0xe6, 0xec, 0x03, 0xdf, 0x12, 0x56, - 0x3e, 0x4d, 0xc6, 0x84, 0xc8, 0x82, 0x94, 0xdb, 0xe6, 0xe4, 0x3d, 0x65, 0x48, 0xca, 0x8d, 0x78, - 0x7e, 0x8c, 0x54, 0x20, 0xa3, 0x0b, 0xd5, 0x56, 0x8d, 0x2d, 0x3f, 0xf5, 0x76, 0x12, 0xe9, 0xdc, - 0xa6, 0xee, 0x42, 0xc5, 0xd1, 0xf0, 0x72, 0x72, 0xab, 0x56, 0x32, 0xd7, 0xb4, 0xcb, 0x49, 0xd6, - 0x46, 0xcd, 0x55, 0x57, 0x20, 0x19, 0xff, 0x68, 0x34, 0x3e, 0x80, 0x62, 0xc3, 0x3b, 0xe1, 0xb7, - 0xf1, 0x16, 0x0c, 0x95, 0x9a, 0x4d, 0xf7, 0x40, 0x48, 0x09, 0x69, 0x65, 0x08, 0xc7, 0x8f, 0xef, - 0x67, 0x36, 0x43, 0xd1, 0x62, 0x62, 0x18, 0x80, 0x94, 0x61, 0xb4, 0xb4, 0x55, 0xab, 0x56, 0x2b, - 0x9b, 0x9b, 0xdc, 0xff, 0x3f, 0xb7, 0xf4, 0x92, 0x1c, 0x1f, 0xc7, 0x69, 0x58, 0xf1, 0xfb, 0xb1, - 0x48, 0x7f, 0x8f, 0xe8, 0xc8, 0x3b, 0x00, 0xb7, 0x5d, 0xa7, 0x7d, 0x97, 0x06, 0x7b, 0x6e, 0x43, - 0x74, 0xfe, 0xdc, 0xa3, 0xa3, 0x85, 0xb1, 0x8f, 0x5c, 0xa7, 0x6d, 0xb5, 0x10, 0xcc, 0xda, 0x1e, - 0x21, 0x99, 0xca, 0xdf, 0x6c, 0xa4, 0x97, 0x5c, 0xee, 0xe0, 0x30, 0x14, 0x8d, 0xf4, 0xb6, 0x9b, - 0xf0, 0x6d, 0x90, 0x68, 0xa4, 0x05, 0x53, 0xb5, 0xee, 0xee, 0x2e, 0x65, 0x92, 0x5d, 0xd8, 0x2d, - 0x86, 0xc5, 0x19, 0x37, 0xcc, 0xba, 0xc0, 0xcf, 0x23, 0xec, 0x94, 0xe2, 0x2f, 0xbd, 0xc6, 0x16, - 0xf2, 0x77, 0x8f, 0x16, 0xc4, 0xbd, 0x1b, 0x53, 0xd5, 0x7c, 0x49, 0x9f, 0xb4, 0x5a, 0xc4, 0x79, - 0x93, 0x75, 0x18, 0xbe, 0xe5, 0x04, 0x2b, 0xdd, 0x6d, 0xe1, 0xcf, 0xfe, 0x42, 0x9f, 0x8f, 0x86, - 0x23, 0x72, 0xc3, 0xef, 0xae, 0x13, 0xec, 0x75, 0x55, 0x8f, 0x62, 0xc1, 0x86, 0x6c, 0x41, 0xa1, - 0xec, 0x78, 0xf5, 0x26, 0x2d, 0x57, 0xc5, 0xde, 0x7f, 0xa1, 0x0f, 0x4b, 0x89, 0xca, 0xc7, 0xa5, - 0x8e, 0xbf, 0xea, 0x8e, 0xaa, 0x0b, 0x48, 0x0c, 0xf2, 0xd7, 0x33, 0xf0, 0x6c, 0xd8, 0xfa, 0xd2, - 0x2e, 0x6d, 0x07, 0x77, 0xed, 0xa0, 0xbe, 0x47, 0x3d, 0x31, 0x4a, 0xa3, 0xfd, 0x46, 0xe9, 0x4b, - 0x89, 0x51, 0xba, 0x14, 0x8d, 0x92, 0xcd, 0x98, 0x59, 0x2d, 0xce, 0x2d, 0x39, 0x66, 0xfd, 0x6a, - 0x25, 0x16, 0x40, 0x64, 0xc9, 0x17, 0xf1, 0x50, 0x2f, 0xf5, 0xe9, 0x70, 0x84, 0x2c, 0xfc, 0x98, - 0xc3, 0xdf, 0x9a, 0x3f, 0x4f, 0x08, 0x25, 0x77, 0x64, 0xf0, 0x08, 0xd7, 0x4a, 0xce, 0xf7, 0xe1, - 0xcd, 0x03, 0x4a, 0x66, 0xfa, 0x84, 0x89, 0xf1, 0xd9, 0x5e, 0xb5, 0xb7, 0x85, 0x22, 0x72, 0xcc, - 0x6c, 0xaf, 0xda, 0xd1, 0x6c, 0x37, 0xed, 0xf8, 0x6c, 0xaf, 0xda, 0xdb, 0xa4, 0xcc, 0x23, 0xde, - 0x78, 0x78, 0xd4, 0xf3, 0xfd, 0xb8, 0x95, 0x37, 0xf8, 0xce, 0x9c, 0x12, 0xf9, 0xf6, 0x21, 0x8c, - 0xd6, 0x3a, 0x76, 0x9d, 0x36, 0x9d, 0x9d, 0x40, 0x5c, 0xed, 0xbc, 0xd8, 0x87, 0x55, 0x88, 0x2b, - 0xae, 0x05, 0xe4, 0x4f, 0xf5, 0x98, 0x14, 0xe2, 0xb0, 0x16, 0x6e, 0x6e, 0xdc, 0x9d, 0x9b, 0x3a, - 0xb6, 0x85, 0x9b, 0x1b, 0x77, 0x85, 0xce, 0xd1, 0x69, 0x69, 0x3a, 0xc7, 0xc6, 0x5d, 0xe3, 0xd7, - 0x72, 0x70, 0xa6, 0x07, 0x0d, 0x59, 0x93, 0x32, 0x2a, 0xa3, 0x99, 0x17, 0x7b, 0xa0, 0x5f, 0x39, - 0x56, 0x6c, 0xad, 0x42, 0x71, 0xf9, 0x0e, 0x2a, 0xb7, 0xec, 0x27, 0x6d, 0x94, 0x4b, 0x52, 0xba, - 0x9f, 0x7f, 0x74, 0xb4, 0xf0, 0x1c, 0xdd, 0x47, 0xd7, 0x20, 0x9b, 0x17, 0x5a, 0x75, 0x2d, 0x78, - 0x2d, 0x41, 0x39, 0xff, 0x03, 0x59, 0xc8, 0xe3, 0x4e, 0x13, 0x4b, 0xd9, 0x91, 0x39, 0x51, 0xca, - 0x8e, 0xf7, 0x61, 0x7c, 0xf9, 0x0e, 0x3f, 0x7a, 0xae, 0xd8, 0xfe, 0x9e, 0x90, 0x83, 0x78, 0xd3, - 0x46, 0xf7, 0x2d, 0x71, 0x52, 0xdd, 0xb3, 0x35, 0x25, 0x4f, 0xa3, 0x20, 0xf7, 0x60, 0x86, 0xb7, - 0xcd, 0xd9, 0x71, 0xea, 0x3c, 0xf2, 0xdf, 0xb1, 0x9b, 0x42, 0x28, 0x5e, 0x78, 0x74, 0xb4, 0xb0, - 0x40, 0xf7, 0xd1, 0xe9, 0x49, 0x94, 0x5b, 0x3e, 0x22, 0xa8, 0xde, 0x4f, 0x29, 0xf4, 0x6a, 0x38, - 0xb2, 0x39, 0x8a, 0x15, 0xb2, 0xda, 0x58, 0xdd, 0x0c, 0x97, 0x23, 0x19, 0xff, 0x60, 0x08, 0xe6, - 0x7b, 0xcb, 0x33, 0xf2, 0x65, 0x7d, 0x02, 0x2f, 0x1e, 0x2b, 0x01, 0x8f, 0x9f, 0xc3, 0xaf, 0xc0, - 0xec, 0x72, 0x3b, 0xa0, 0x5e, 0xc7, 0x73, 0x64, 0x00, 0xfa, 0x8a, 0xeb, 0x4b, 0x27, 0x33, 0xf4, - 0xf6, 0xa2, 0x61, 0xb9, 0xb0, 0x12, 0xa2, 0xcb, 0x9b, 0xc2, 0x2a, 0x95, 0x03, 0x59, 0x86, 0x49, - 0x05, 0xde, 0xec, 0xee, 0x8a, 0x1d, 0x1c, 0x3d, 0x18, 0x55, 0x9e, 0xcd, 0xae, 0xea, 0x81, 0x13, - 0x23, 0x9a, 0xff, 0x85, 0x9c, 0x58, 0x16, 0x17, 0x20, 0x57, 0xeb, 0x6e, 0x8b, 0xe5, 0xc0, 0x55, - 0x75, 0x4d, 0xac, 0xb3, 0x52, 0xf2, 0x45, 0x00, 0x93, 0x76, 0x5c, 0xdf, 0x09, 0x5c, 0xef, 0x50, - 0x8d, 0x63, 0xf0, 0x42, 0xa8, 0xee, 0xb1, 0x29, 0xa1, 0x64, 0x05, 0xa6, 0xa2, 0x5f, 0xeb, 0x07, - 0x6d, 0x61, 0xda, 0x1c, 0xe5, 0x36, 0x85, 0x88, 0xdc, 0x72, 0x59, 0x99, 0xba, 0x51, 0xc5, 0xc8, - 0xc8, 0x22, 0x14, 0xb6, 0x5c, 0x6f, 0x7f, 0x87, 0x4d, 0x54, 0x3e, 0xda, 0x4a, 0x0f, 0x04, 0x4c, - 0xdd, 0x32, 0x24, 0x1e, 0x5b, 0xf3, 0xcb, 0xed, 0x07, 0x8e, 0xe7, 0xb6, 0x5b, 0xb4, 0x1d, 0xa8, - 0xb7, 0x90, 0x34, 0x02, 0x6b, 0x11, 0x64, 0x11, 0x98, 0x9d, 0x9c, 0x4b, 0xf5, 0xc0, 0xf5, 0xc4, - 0x15, 0x24, 0x9f, 0x6e, 0x06, 0xd0, 0xa6, 0x9b, 0x01, 0xd8, 0x20, 0x9a, 0x74, 0x47, 0xd8, 0xce, - 0x71, 0x10, 0x3d, 0xba, 0xa3, 0x85, 0xc7, 0xd1, 0x1d, 0xa6, 0x0a, 0x98, 0x74, 0x07, 0x8f, 0xfb, - 0x5a, 0x56, 0x99, 0x9d, 0x84, 0xa1, 0x48, 0xa0, 0x19, 0xbf, 0x33, 0xda, 0x73, 0xdd, 0x32, 0xd9, - 0x7b, 0xb2, 0x75, 0xbb, 0x6a, 0x0f, 0xb0, 0x6e, 0x5f, 0x0b, 0xfd, 0x40, 0xd5, 0x98, 0x50, 0x84, - 0xa8, 0xc2, 0x9f, 0xe3, 0xcc, 0xff, 0x62, 0xe1, 0x24, 0x8b, 0x48, 0x0c, 0x52, 0x76, 0xd0, 0x41, - 0xca, 0x0d, 0x34, 0x48, 0x64, 0x09, 0x26, 0xc2, 0xbc, 0x44, 0x1b, 0x76, 0xa0, 0xc9, 0xa6, 0x30, - 0x99, 0x94, 0xd5, 0xb1, 0x03, 0x55, 0x36, 0xe9, 0x24, 0xe4, 0x6d, 0x18, 0x13, 0xce, 0xd0, 0xc8, - 0x61, 0x28, 0x72, 0x47, 0x93, 0x9e, 0xd3, 0x31, 0x7a, 0x15, 0x9d, 0x7d, 0x92, 0x1b, 0x4e, 0x87, - 0x36, 0x9d, 0x36, 0xad, 0xa1, 0xed, 0x5c, 0xac, 0x18, 0xfc, 0x24, 0x3b, 0xa2, 0xc4, 0xe2, 0x66, - 0x75, 0xcd, 0x6a, 0xa6, 0x11, 0xc5, 0x17, 0xeb, 0xc8, 0x89, 0x16, 0x2b, 0xf7, 0x06, 0xf1, 0x56, - 0xdd, 0x5d, 0x47, 0xfa, 0xbf, 0x49, 0x6f, 0x10, 0xcf, 0x6a, 0x32, 0x68, 0xcc, 0x1b, 0x84, 0xa3, - 0x32, 0xbd, 0x9e, 0xfd, 0xa8, 0x56, 0xc4, 0x3d, 0x0d, 0xea, 0xf5, 0x48, 0xa4, 0x3b, 0x1d, 0x72, - 0x24, 0x59, 0xcd, 0x72, 0xcb, 0x76, 0x9a, 0x22, 0xf4, 0x2f, 0xaa, 0x86, 0x32, 0x68, 0xbc, 0x1a, - 0x44, 0x25, 0x75, 0x18, 0x37, 0xe9, 0xce, 0x86, 0xe7, 0x06, 0xb4, 0x1e, 0xd0, 0x86, 0xd0, 0x65, - 0xa4, 0x3a, 0xbf, 0xe4, 0xba, 0x5c, 0x4f, 0x5b, 0x7a, 0xfd, 0x77, 0x8e, 0x16, 0x32, 0xdf, 0x3d, - 0x5a, 0x00, 0x06, 0xe2, 0x1e, 0xad, 0x8f, 0x8e, 0x16, 0xce, 0xb0, 0xf9, 0xef, 0x48, 0x62, 0x75, - 0x8b, 0x51, 0x99, 0x92, 0xef, 0x65, 0x42, 0x37, 0x1c, 0x92, 0xa8, 0xb2, 0xf1, 0x1e, 0x95, 0xbd, - 0x91, 0x5a, 0xd9, 0x82, 0x32, 0xda, 0xa9, 0x95, 0xa6, 0x56, 0x42, 0xde, 0x81, 0xb1, 0x72, 0xb5, - 0xec, 0xb6, 0x77, 0x9c, 0xdd, 0xda, 0x4a, 0x09, 0x15, 0x22, 0xe1, 0xcd, 0x5c, 0x77, 0xac, 0x3a, - 0xc2, 0x2d, 0x7f, 0xcf, 0xd6, 0x82, 0x5a, 0x22, 0x7c, 0x72, 0x0b, 0x26, 0xe5, 0x4f, 0x93, 0xee, - 0xdc, 0x33, 0xab, 0xa8, 0x07, 0x49, 0x17, 0xf2, 0x90, 0x03, 0x1b, 0x88, 0xae, 0xa7, 0xea, 0xc7, - 0x31, 0x32, 0xb6, 0x18, 0x2b, 0xb4, 0xd3, 0x74, 0x0f, 0x59, 0xf3, 0x36, 0x1d, 0xea, 0xa1, 0xe6, - 0x23, 0x16, 0x63, 0x23, 0x2c, 0xb1, 0x02, 0x47, 0x13, 0xb7, 0x31, 0x22, 0xb2, 0x06, 0xd3, 0x62, - 0x89, 0xdf, 0x77, 0x7c, 0x67, 0xdb, 0x69, 0x3a, 0xc1, 0x21, 0x86, 0x19, 0x0a, 0x2d, 0x44, 0x7e, - 0x17, 0x0f, 0xc2, 0x52, 0x85, 0x59, 0x92, 0xd4, 0xf8, 0xe5, 0x2c, 0x3c, 0xd7, 0x4f, 0xff, 0x27, - 0x35, 0x5d, 0x98, 0x5d, 0x1a, 0xe0, 0xcc, 0x70, 0xbc, 0x38, 0x5b, 0x86, 0xc9, 0x75, 0x6f, 0xd7, - 0x6e, 0x3b, 0xdf, 0xc2, 0x73, 0x5d, 0xe8, 0x14, 0x83, 0x83, 0xe1, 0x2a, 0x25, 0xfa, 0x6a, 0x8f, - 0x11, 0xcd, 0x3f, 0x10, 0x62, 0xee, 0x93, 0x86, 0x57, 0xdc, 0x80, 0xd1, 0xb2, 0xdb, 0x0e, 0xe8, - 0xc3, 0x20, 0x16, 0x05, 0xc8, 0x81, 0xf1, 0xd0, 0x12, 0x89, 0x6a, 0xfc, 0xbf, 0x59, 0x38, 0xd7, - 0x57, 0x01, 0x26, 0x9b, 0xfa, 0xa8, 0x5d, 0x1e, 0x44, 0x6b, 0x3e, 0x7e, 0xd8, 0x16, 0x13, 0xfe, - 0x1b, 0xc7, 0x7a, 0x2f, 0xcf, 0xff, 0xd7, 0x19, 0x31, 0x48, 0x9f, 0x83, 0x11, 0xac, 0x2a, 0x1c, - 0x22, 0x6e, 0x1b, 0x42, 0x29, 0xec, 0xe8, 0xb6, 0x21, 0x8e, 0x46, 0xae, 0x43, 0xa1, 0x6c, 0x37, - 0x9b, 0x4a, 0x8c, 0x24, 0xea, 0xf5, 0x75, 0x84, 0xc5, 0xdc, 0x7d, 0x24, 0x22, 0x79, 0x13, 0x80, - 0xff, 0xad, 0xec, 0x15, 0x28, 0x2c, 0x05, 0x59, 0x6c, 0xbb, 0x50, 0x90, 0x31, 0xb3, 0x5a, 0xdd, - 0x0d, 0x83, 0xb9, 0x78, 0x66, 0x35, 0x06, 0xd0, 0x32, 0xab, 0x31, 0x80, 0xf1, 0x2b, 0x39, 0x78, - 0xbe, 0xff, 0x29, 0x8e, 0xdc, 0xd3, 0xa7, 0xe0, 0x95, 0x81, 0xce, 0x7e, 0xc7, 0xcf, 0x81, 0xcc, - 0x53, 0xc8, 0x07, 0xe4, 0x52, 0xd2, 0xc9, 0xf8, 0xe3, 0xa3, 0x05, 0xc5, 0x87, 0xec, 0xb6, 0xeb, - 0xb4, 0x95, 0x9b, 0x82, 0x6f, 0x02, 0xd4, 0x02, 0x3b, 0x70, 0xea, 0xb7, 0xb7, 0xee, 0xc8, 0x30, - 0xfe, 0x1b, 0x83, 0xb5, 0x2c, 0xa2, 0xe3, 0x72, 0x45, 0xc4, 0x37, 0x20, 0xd4, 0xfa, 0xe8, 0x60, - 0x5f, 0x3b, 0xa7, 0x46, 0xc8, 0xf3, 0x5f, 0x82, 0x62, 0x9c, 0x94, 0x5c, 0x84, 0x3c, 0x36, 0x40, - 0xf1, 0x94, 0x8e, 0x71, 0xc0, 0xf2, 0xf9, 0xbb, 0x62, 0xed, 0x2c, 0xc3, 0xa4, 0xb8, 0x9e, 0xd6, - 0x2d, 0x62, 0xf8, 0xbd, 0xca, 0xdb, 0xed, 0xa4, 0x55, 0x2c, 0x46, 0x64, 0xfc, 0x69, 0x06, 0xce, - 0xf6, 0x3c, 0x1f, 0x93, 0x0d, 0x7d, 0xc2, 0x5e, 0x3a, 0xee, 0x40, 0x7d, 0xec, 0x5c, 0xcd, 0xff, - 0xa8, 0x5c, 0xfb, 0xef, 0xc2, 0x78, 0xad, 0xbb, 0x1d, 0x3f, 0x64, 0xf1, 0xa0, 0x6e, 0x05, 0xae, - 0xee, 0x60, 0x2a, 0x3e, 0xeb, 0xbf, 0xbc, 0x7f, 0x17, 0xee, 0x15, 0xfc, 0xe0, 0x87, 0xfd, 0x0f, - 0xc3, 0xa3, 0x30, 0x00, 0x4f, 0x1d, 0xc4, 0x18, 0x91, 0xf1, 0x4b, 0xd9, 0xf4, 0xd3, 0x2a, 0x3b, - 0x6b, 0x9f, 0xe0, 0xb4, 0x7a, 0xab, 0xbc, 0x71, 0x7c, 0xdf, 0xff, 0x53, 0xd9, 0x77, 0xbc, 0x8e, - 0x14, 0x12, 0x4f, 0x9a, 0xf7, 0xc4, 0x75, 0xa4, 0x94, 0x8e, 0xbe, 0x7e, 0x1d, 0x29, 0x91, 0xc9, - 0x1b, 0x30, 0xba, 0xea, 0xf2, 0xc0, 0x58, 0xd9, 0x63, 0x1e, 0x3f, 0x24, 0x81, 0xaa, 0x78, 0x0c, - 0x31, 0xd9, 0xd9, 0x42, 0x9f, 0x78, 0xe9, 0xe4, 0x8d, 0x67, 0x8b, 0xd8, 0x72, 0xd1, 0x8d, 0x60, - 0x3a, 0x99, 0xf1, 0xa3, 0x59, 0x98, 0xe4, 0x8b, 0x97, 0x1b, 0x69, 0x9f, 0x5a, 0x03, 0xf8, 0x5b, - 0x9a, 0x01, 0x5c, 0xe6, 0x7f, 0x50, 0xbb, 0x36, 0x90, 0xf9, 0x7b, 0x0f, 0x48, 0x92, 0x86, 0x98, - 0x30, 0xae, 0x42, 0xfb, 0x5b, 0xbe, 0xaf, 0x45, 0xa9, 0x42, 0x84, 0xec, 0xc0, 0xeb, 0x07, 0xdf, - 0xd4, 0x78, 0x18, 0x7f, 0x35, 0x0b, 0x13, 0xca, 0x75, 0xe5, 0x53, 0x3b, 0xf0, 0x5f, 0xd2, 0x06, - 0x7e, 0x2e, 0x74, 0x4c, 0x0e, 0x7b, 0x36, 0xd0, 0xb8, 0x77, 0x61, 0x3a, 0x41, 0x12, 0xbf, 0xf5, - 0xcd, 0x0c, 0x72, 0xeb, 0xfb, 0x5a, 0x32, 0xef, 0x00, 0x4f, 0xdf, 0x19, 0x06, 0xb3, 0xaa, 0x89, - 0x0e, 0x7e, 0x3c, 0x0b, 0xb3, 0xe2, 0x17, 0x26, 0xea, 0xe1, 0xd2, 0xfb, 0xa9, 0x9d, 0x8b, 0x92, - 0x36, 0x17, 0x0b, 0xfa, 0x5c, 0x28, 0x1d, 0xec, 0x3d, 0x25, 0xc6, 0x5f, 0x06, 0x98, 0xeb, 0x45, - 0x30, 0x70, 0xfc, 0x4f, 0xe4, 0x5d, 0x9d, 0x1d, 0xc0, 0xbb, 0x7a, 0x15, 0x8a, 0x58, 0x95, 0x48, - 0xc5, 0xe1, 0xb3, 0x33, 0x40, 0x2e, 0x52, 0xb8, 0x79, 0x36, 0x25, 0x91, 0x1a, 0xc4, 0x8f, 0x1d, - 0x02, 0x12, 0x94, 0xe4, 0x17, 0x32, 0x30, 0x89, 0xc0, 0xe5, 0x07, 0xb4, 0x1d, 0x20, 0xb3, 0xbc, - 0x70, 0x06, 0x0e, 0xed, 0xe3, 0xb5, 0xc0, 0x73, 0xda, 0xbb, 0xc2, 0x40, 0xbe, 0x2d, 0x0c, 0xe4, - 0x6f, 0x73, 0xc3, 0xfe, 0x95, 0xba, 0xdb, 0xba, 0xba, 0xeb, 0xd9, 0x0f, 0x1c, 0x7e, 0x13, 0x6f, - 0x37, 0xaf, 0x46, 0xd9, 0xa3, 0x3b, 0x4e, 0x2c, 0x1f, 0xb4, 0x60, 0x85, 0x97, 0x0f, 0xbc, 0xa1, - 0x14, 0xab, 0x8d, 0x9f, 0x55, 0xf4, 0x16, 0x91, 0xef, 0x81, 0x33, 0x3c, 0xce, 0x9e, 0xa9, 0xbc, - 0x4e, 0xbb, 0xeb, 0x76, 0xfd, 0x25, 0xbb, 0xbe, 0xcf, 0xf6, 0x3d, 0x1e, 0xd0, 0x80, 0x3d, 0xaf, - 0x87, 0x85, 0xd6, 0x36, 0x2f, 0xd5, 0x02, 0xb8, 0xd2, 0x19, 0x90, 0x15, 0x98, 0xe6, 0x45, 0xa5, - 0x6e, 0xe0, 0xd6, 0xea, 0x76, 0xd3, 0x69, 0xef, 0xe2, 0x99, 0xba, 0xc0, 0xf7, 0x63, 0xbb, 0x1b, - 0xb8, 0x96, 0xcf, 0xe1, 0xea, 0xd1, 0x25, 0x41, 0x44, 0xaa, 0x30, 0x65, 0x52, 0xbb, 0x71, 0xd7, - 0x7e, 0x58, 0xb6, 0x3b, 0x76, 0x9d, 0x1d, 0x84, 0x0a, 0x78, 0x99, 0x84, 0x67, 0x33, 0x8f, 0xda, - 0x0d, 0xab, 0x65, 0x3f, 0xb4, 0xea, 0xa2, 0x50, 0xb7, 0x61, 0x69, 0x74, 0x21, 0x2b, 0xa7, 0x1d, - 0xb2, 0x1a, 0x8d, 0xb3, 0x72, 0xda, 0xbd, 0x59, 0x45, 0x74, 0x92, 0xd5, 0xa6, 0xed, 0xed, 0xd2, - 0x80, 0x3b, 0xb2, 0xb1, 0xf3, 0x78, 0x46, 0x61, 0x15, 0x60, 0x99, 0x85, 0x4e, 0x6d, 0x71, 0x56, - 0x0a, 0x1d, 0x5b, 0x79, 0x5b, 0x9e, 0x13, 0x50, 0xb5, 0x87, 0x63, 0xd8, 0x2c, 0x1c, 0x7f, 0x74, - 0x01, 0xec, 0xd5, 0xc5, 0x04, 0x65, 0xc4, 0x4d, 0xe9, 0xe4, 0x78, 0x82, 0x5b, 0x7a, 0x2f, 0x13, - 0x94, 0x21, 0x37, 0xb5, 0x9f, 0x13, 0xd8, 0x4f, 0x85, 0x5b, 0x8f, 0x8e, 0x26, 0x28, 0xc9, 0x1a, - 0x1b, 0xb4, 0x80, 0xb6, 0xd9, 0x8a, 0x16, 0x8e, 0x7c, 0x93, 0xd8, 0xb4, 0x17, 0x85, 0x37, 0x4a, - 0xd1, 0x93, 0xc5, 0x56, 0x8a, 0x5b, 0x5f, 0x9c, 0x98, 0x7c, 0x1f, 0x4c, 0xdd, 0xf3, 0xe9, 0xcd, - 0xea, 0x46, 0x4d, 0x86, 0xe5, 0xe3, 0x69, 0x7b, 0x72, 0xf1, 0xda, 0x31, 0x42, 0xe7, 0x8a, 0x4a, - 0x83, 0x49, 0x9c, 0xf9, 0xbc, 0x75, 0x7d, 0x6a, 0xed, 0x38, 0x1d, 0x3f, 0xcc, 0x71, 0xa2, 0xce, - 0x5b, 0xac, 0x2a, 0x63, 0x05, 0xa6, 0x13, 0x6c, 0xc8, 0x24, 0x00, 0x03, 0x5a, 0xf7, 0xd6, 0x6a, - 0xcb, 0x9b, 0xc5, 0x67, 0x48, 0x11, 0xc6, 0xf1, 0xf7, 0xf2, 0x5a, 0x69, 0x69, 0x75, 0xb9, 0x52, - 0xcc, 0x90, 0x69, 0x98, 0x40, 0x48, 0xa5, 0x5a, 0xe3, 0xa0, 0x2c, 0x4f, 0xe1, 0x69, 0x16, 0xf9, - 0xa7, 0x1b, 0xb0, 0x0f, 0x00, 0xf7, 0x14, 0xe3, 0x6f, 0x66, 0xe1, 0xac, 0xdc, 0x56, 0x68, 0x70, - 0xe0, 0x7a, 0xfb, 0x4e, 0x7b, 0xf7, 0x29, 0xdf, 0x1d, 0x6e, 0x6a, 0xbb, 0xc3, 0x8b, 0xb1, 0x9d, - 0x3a, 0xd6, 0xcb, 0x3e, 0x5b, 0xc4, 0x3f, 0x2b, 0xc0, 0xb9, 0xbe, 0x54, 0xe4, 0xcb, 0x6c, 0x37, - 0x77, 0x68, 0x3b, 0xa8, 0x36, 0x9a, 0x74, 0xd3, 0x69, 0x51, 0xb7, 0x1b, 0x08, 0xc7, 0xd1, 0x0b, - 0x78, 0xc0, 0xc5, 0x42, 0xcb, 0x69, 0x34, 0xa9, 0x15, 0xf0, 0x62, 0x6d, 0xb9, 0x25, 0xa9, 0x19, - 0xcb, 0x30, 0xa1, 0x7c, 0xb5, 0x1d, 0x50, 0xef, 0x01, 0x3a, 0xa7, 0x84, 0x2c, 0xf7, 0x29, 0xed, - 0x58, 0x36, 0x2b, 0xb5, 0x1c, 0x51, 0xac, 0xb3, 0x4c, 0x50, 0x93, 0x9b, 0x0a, 0xcb, 0x32, 0x53, - 0x87, 0xef, 0xda, 0x0f, 0xc5, 0x6d, 0xb9, 0xc8, 0xcf, 0x14, 0xb2, 0xe4, 0x31, 0x47, 0x2d, 0xfb, - 0xa1, 0x99, 0x24, 0x21, 0x5f, 0x83, 0x53, 0x62, 0x03, 0x12, 0x31, 0xa3, 0xb2, 0xc7, 0x3c, 0x22, - 0xf5, 0xe5, 0x47, 0x47, 0x0b, 0x67, 0x64, 0x66, 0x2b, 0x19, 0x25, 0x9c, 0xd6, 0xeb, 0x74, 0x2e, - 0x64, 0x93, 0x6d, 0xc8, 0xb1, 0xe1, 0xb8, 0x4b, 0x7d, 0xdf, 0xde, 0x95, 0x37, 0xeb, 0xdc, 0xcb, - 0x5e, 0x19, 0x4c, 0xab, 0xc5, 0xcb, 0xcd, 0x9e, 0x94, 0x64, 0x05, 0x26, 0xb7, 0xe8, 0xb6, 0x3a, - 0x3f, 0xc3, 0xa1, 0xa8, 0x2a, 0x1e, 0xd0, 0xed, 0xde, 0x93, 0x13, 0xa3, 0x23, 0x0e, 0x1a, 0xcc, - 0x1e, 0x1e, 0xae, 0x3a, 0x7e, 0x40, 0xdb, 0xd4, 0xc3, 0x5c, 0x04, 0x23, 0x28, 0x0c, 0xe6, 0x22, - 0x0d, 0x59, 0x2f, 0x5f, 0x7a, 0xe1, 0xd1, 0xd1, 0xc2, 0x39, 0x1e, 0x55, 0xd2, 0x14, 0x70, 0x2b, - 0x96, 0x8e, 0x3d, 0xc9, 0x95, 0x7c, 0x03, 0xa6, 0x4c, 0xb7, 0x1b, 0x38, 0xed, 0xdd, 0x5a, 0xe0, - 0xd9, 0x01, 0xdd, 0xe5, 0x1b, 0x52, 0x94, 0xf4, 0x20, 0x56, 0x2a, 0xee, 0x5a, 0x38, 0xd0, 0xf2, - 0x05, 0x54, 0xdb, 0x11, 0x74, 0x02, 0xf2, 0x75, 0x98, 0xe4, 0xd1, 0x82, 0x61, 0x05, 0xa3, 0x5a, - 0x2a, 0x59, 0xbd, 0xf0, 0xfe, 0x35, 0x7e, 0x40, 0xe5, 0x51, 0x87, 0x69, 0x15, 0xc4, 0xb8, 0x91, - 0x0f, 0xc5, 0x60, 0x6d, 0x38, 0xed, 0xdd, 0x70, 0x19, 0x03, 0x8e, 0xfc, 0xeb, 0xd1, 0x90, 0x74, - 0x58, 0x73, 0xe5, 0x32, 0xee, 0xe1, 0xa9, 0x91, 0xe4, 0x43, 0x02, 0x38, 0x57, 0xf2, 0x7d, 0xc7, - 0x0f, 0x84, 0x7b, 0xf5, 0xf2, 0x43, 0x5a, 0xef, 0x32, 0xe4, 0x2d, 0xd7, 0xdb, 0xa7, 0x1e, 0x77, - 0xef, 0x1b, 0x5a, 0xba, 0xf2, 0xe8, 0x68, 0xe1, 0x15, 0x1b, 0x11, 0x2d, 0xe1, 0x91, 0x6d, 0x51, - 0x89, 0x6a, 0x1d, 0x70, 0x5c, 0xa5, 0x0f, 0xfd, 0x99, 0x92, 0xaf, 0xc3, 0xe9, 0xb2, 0xed, 0xd3, - 0x6a, 0xdb, 0xa7, 0x6d, 0xdf, 0x09, 0x9c, 0x07, 0x54, 0x0c, 0x2a, 0x6e, 0x7e, 0x05, 0x4c, 0x5c, - 0x6f, 0xd4, 0x6d, 0x9f, 0x7d, 0x98, 0x21, 0x8a, 0x25, 0x26, 0x45, 0xa9, 0xa6, 0x07, 0x17, 0xe3, - 0x28, 0x03, 0xc5, 0xf8, 0xb0, 0x93, 0xaf, 0xc0, 0x28, 0x77, 0x49, 0xa0, 0xfe, 0x9e, 0x08, 0x74, - 0x93, 0x37, 0xdc, 0x21, 0x5c, 0x27, 0x12, 0xa1, 0x09, 0xdc, 0xe1, 0x81, 0xaa, 0xf7, 0xb5, 0x18, - 0x9a, 0x20, 0x89, 0x48, 0x03, 0xc6, 0xf9, 0xc8, 0x52, 0xcc, 0x4e, 0x22, 0x3c, 0xd3, 0x5e, 0x50, - 0x57, 0xb2, 0x28, 0x8a, 0xf1, 0x47, 0x93, 0xb7, 0x98, 0x3f, 0x8e, 0xa0, 0x55, 0xa1, 0x71, 0x5d, - 0x02, 0x28, 0x48, 0x42, 0xe3, 0x2c, 0x9c, 0xe9, 0xd1, 0x66, 0xe3, 0x01, 0x5e, 0x83, 0xf5, 0xa8, - 0x91, 0x7c, 0x05, 0x66, 0x91, 0xb0, 0xec, 0xb6, 0xdb, 0xb4, 0x1e, 0xa0, 0xe8, 0x90, 0xa6, 0xa3, - 0x1c, 0xbf, 0x6b, 0xe5, 0xfd, 0xad, 0x87, 0x08, 0x56, 0xdc, 0x82, 0x94, 0xca, 0xc1, 0xf8, 0x99, - 0x2c, 0xcc, 0x09, 0x69, 0x64, 0xd2, 0xba, 0xeb, 0x35, 0x9e, 0xfe, 0xdd, 0x6f, 0x59, 0xdb, 0xfd, - 0x2e, 0x84, 0x91, 0xcd, 0x69, 0x9d, 0xec, 0xb3, 0xf9, 0xfd, 0x52, 0x06, 0x9e, 0xeb, 0x47, 0xc4, - 0x46, 0x27, 0xcc, 0xc6, 0x32, 0x9a, 0xc8, 0xba, 0xd2, 0x81, 0x19, 0x9c, 0xd0, 0xf2, 0x1e, 0xad, - 0xef, 0xfb, 0x2b, 0xae, 0x1f, 0xa0, 0x73, 0x6c, 0xb6, 0xc7, 0x45, 0xcd, 0x6b, 0xa9, 0x17, 0x35, - 0xa7, 0xf9, 0x2a, 0xab, 0x23, 0x0f, 0x9e, 0x2f, 0x66, 0x9f, 0x1e, 0xfa, 0x66, 0x1a, 0x6b, 0x74, - 0x72, 0x2c, 0x75, 0x83, 0xbd, 0x0d, 0x8f, 0xee, 0x50, 0x8f, 0xb6, 0xeb, 0xf4, 0x33, 0xe6, 0xe4, - 0xa8, 0x77, 0x6e, 0x20, 0x6b, 0xc3, 0xbf, 0x36, 0x01, 0xb3, 0x69, 0x64, 0x6c, 0x5c, 0x94, 0x03, - 0x6e, 0xfc, 0x0d, 0x9c, 0x1f, 0xca, 0xc0, 0x78, 0x8d, 0xd6, 0xdd, 0x76, 0xe3, 0x26, 0x5e, 0x87, - 0x8b, 0xd1, 0xb1, 0xf8, 0x06, 0xcf, 0xe0, 0xd6, 0x4e, 0xec, 0x9e, 0xfc, 0xe3, 0xa3, 0x85, 0xf7, - 0x07, 0x3b, 0x57, 0xd6, 0x5d, 0x8c, 0x4e, 0x0e, 0x30, 0x47, 0x6b, 0x58, 0x05, 0x5a, 0xb6, 0xb5, - 0x4a, 0xc9, 0x12, 0x4c, 0x88, 0xcf, 0xd5, 0x55, 0x93, 0xf1, 0xf0, 0xe0, 0x6f, 0x59, 0x90, 0x48, - 0xa0, 0xa6, 0x91, 0x90, 0xeb, 0x90, 0xbb, 0xb7, 0x78, 0x53, 0xcc, 0x81, 0xcc, 0x72, 0x7b, 0x6f, - 0xf1, 0x26, 0x9a, 0xae, 0xd8, 0x71, 0x60, 0xa2, 0xbb, 0xa8, 0xdd, 0x50, 0xdf, 0x5b, 0xbc, 0x49, - 0xbe, 0x1f, 0x4e, 0x55, 0x1c, 0x5f, 0x54, 0xc1, 0xdd, 0x6d, 0x1b, 0x18, 0x64, 0x32, 0xdc, 0x63, - 0xf5, 0x7e, 0x21, 0x75, 0xf5, 0xbe, 0xd0, 0x08, 0x99, 0x58, 0xdc, 0x97, 0xb7, 0x11, 0x4f, 0x3a, - 0x94, 0x5e, 0x0f, 0xf9, 0x08, 0x26, 0xd1, 0xf4, 0x8a, 0x1e, 0xc8, 0x98, 0xe7, 0x71, 0xa4, 0x47, - 0xcd, 0x9f, 0x4b, 0xad, 0x79, 0x1e, 0x2d, 0xb9, 0x16, 0xfa, 0x31, 0x63, 0x4e, 0x48, 0xed, 0x84, - 0xae, 0x71, 0x26, 0xb7, 0x61, 0x4a, 0xa8, 0x4a, 0xeb, 0x3b, 0x9b, 0x7b, 0xb4, 0x62, 0x1f, 0x8a, - 0xcb, 0x65, 0x3c, 0x7d, 0x09, 0xfd, 0xca, 0x72, 0x77, 0xac, 0x60, 0x8f, 0x5a, 0x0d, 0x5b, 0x53, - 0x2a, 0x62, 0x84, 0xe4, 0x7b, 0x61, 0x6c, 0xd5, 0xad, 0x33, 0x2d, 0x19, 0x25, 0x03, 0xbf, 0x6f, - 0xfe, 0x00, 0x5f, 0x59, 0xe1, 0xe0, 0x98, 0xea, 0xf3, 0xf1, 0xd1, 0xc2, 0x5b, 0x27, 0x5d, 0x34, - 0x4a, 0x05, 0xa6, 0x5a, 0x1b, 0x29, 0x43, 0x61, 0x8b, 0x6e, 0xb3, 0xde, 0xc6, 0x5f, 0x60, 0x90, - 0x60, 0xe1, 0x4e, 0x22, 0x7e, 0x69, 0xee, 0x24, 0x02, 0x46, 0x3c, 0x98, 0xc6, 0xf1, 0xd9, 0xb0, - 0x7d, 0xff, 0xc0, 0xf5, 0x1a, 0x98, 0x6a, 0xb7, 0xd7, 0x55, 0xf6, 0x62, 0xea, 0xe0, 0x3f, 0xc7, - 0x07, 0xbf, 0xa3, 0x70, 0x50, 0x95, 0xbd, 0x04, 0x7b, 0xf2, 0x0d, 0x98, 0x34, 0xe9, 0x37, 0xbb, - 0x8e, 0x47, 0xef, 0xde, 0x2c, 0xe1, 0x57, 0x39, 0xae, 0x85, 0xea, 0xe8, 0x85, 0x5c, 0xa3, 0xf4, - 0x38, 0x4c, 0x5a, 0x8b, 0xac, 0xd6, 0x8e, 0xad, 0xdf, 0x16, 0xa8, 0x24, 0x64, 0x03, 0xc6, 0x2a, - 0xf4, 0x81, 0x53, 0xa7, 0x18, 0x4e, 0x20, 0x5c, 0xf9, 0xc2, 0x14, 0xf2, 0x51, 0x09, 0xb7, 0x9b, - 0x34, 0x10, 0xc0, 0x83, 0x13, 0x74, 0x6f, 0xb1, 0x10, 0x91, 0xdc, 0x80, 0x5c, 0xb5, 0xb2, 0x21, - 0x3c, 0xf9, 0xa4, 0x87, 0x7e, 0xb5, 0xb1, 0x21, 0x13, 0x6e, 0xa3, 0xf3, 0x87, 0xd3, 0xd0, 0xfc, - 0x00, 0xab, 0x95, 0x0d, 0xb2, 0x03, 0x13, 0x38, 0x00, 0x2b, 0xd4, 0xe6, 0x63, 0x3b, 0xd5, 0x63, - 0x6c, 0xaf, 0xa4, 0x8e, 0xed, 0x1c, 0x1f, 0xdb, 0x3d, 0x41, 0xad, 0x65, 0x10, 0x56, 0xd9, 0x32, - 0xf5, 0x53, 0x64, 0x35, 0x97, 0x79, 0x6f, 0x37, 0x57, 0xf1, 0x72, 0x5b, 0xa8, 0x9f, 0x32, 0x09, - 0x7a, 0x98, 0x88, 0xb7, 0xa7, 0xa3, 0x70, 0x92, 0x0f, 0xf9, 0x12, 0xe4, 0xd7, 0xf7, 0x03, 0x7b, - 0x6e, 0x5a, 0x1b, 0x47, 0x06, 0x92, 0xdd, 0x47, 0x8b, 0xa1, 0xbb, 0xaf, 0xa5, 0xa5, 0x40, 0x1a, + 0x76, 0x20, 0x36, 0xfd, 0x01, 0xa0, 0xf1, 0xf0, 0xd5, 0x48, 0x80, 0x24, 0x08, 0x92, 0x03, 0x4e, + 0x71, 0x86, 0x43, 0xce, 0x07, 0xb9, 0x04, 0x77, 0xb8, 0x3b, 0x3b, 0x9f, 0xdd, 0x68, 0x90, 0x68, + 0x12, 0x04, 0xb0, 0xd5, 0x20, 0xb1, 0xa3, 0xd9, 0xd9, 0xda, 0x42, 0x77, 0x02, 0xa8, 0x61, 0x77, + 0x57, 0x6f, 0x55, 0x35, 0x41, 0xac, 0x74, 0xd6, 0xd7, 0xe9, 0xd6, 0xf2, 0x9d, 0xbe, 0x7c, 0x92, + 0xa5, 0x73, 0xdc, 0x29, 0x14, 0x3a, 0xdf, 0x59, 0xfe, 0x38, 0x85, 0x43, 0xd2, 0x85, 0xcf, 0xa1, + 0xb0, 0x2c, 0x39, 0x64, 0x85, 0x2c, 0xfb, 0xce, 0x8a, 0xf0, 0xf7, 0x5a, 0x01, 0x59, 0xd6, 0xc5, + 0x85, 0x83, 0x71, 0x76, 0xe8, 0x7c, 0x11, 0x0a, 0x7b, 0x1c, 0xb2, 0x1d, 0xf9, 0x32, 0xb3, 0x2a, + 0xb3, 0xaa, 0xba, 0xd1, 0x18, 0x72, 0xee, 0xc4, 0xb1, 0xfe, 0x90, 0xe8, 0x97, 0xef, 0xbd, 0xfc, + 0xac, 0x97, 0x2f, 0x5f, 0xbe, 0xf7, 0x12, 0x5e, 0x08, 0x68, 0x93, 0x76, 0x5c, 0x2f, 0xb8, 0xda, + 0xa4, 0xbb, 0x76, 0xfd, 0xe0, 0x6a, 0x70, 0xd0, 0xa1, 0x3e, 0xff, 0xf7, 0x4a, 0xc7, 0x73, 0x03, + 0x97, 0x0c, 0xe1, 0x8f, 0xf9, 0xd9, 0x5d, 0x77, 0xd7, 0x45, 0xc8, 0x55, 0xf6, 0x17, 0x2f, 0x9c, + 0x5f, 0xd8, 0x75, 0xdd, 0xdd, 0x26, 0xbd, 0x8a, 0xbf, 0xb6, 0xbb, 0x3b, 0x57, 0x03, 0xa7, 0x45, + 0xfd, 0xc0, 0x6e, 0x75, 0x04, 0xc2, 0xe5, 0xb0, 0x02, 0x3b, 0x08, 0x58, 0x49, 0xe0, 0xb8, 0xed, + 0xab, 0x0f, 0xaf, 0xa9, 0x3f, 0x05, 0xea, 0xeb, 0xe9, 0x6d, 0xd9, 0xf7, 0xec, 0x4e, 0x87, 0x7a, + 0xd1, 0x1f, 0x1c, 0xdd, 0xf8, 0xf9, 0x1c, 0x8c, 0xde, 0xa1, 0xb4, 0x53, 0x6a, 0x3a, 0x0f, 0x29, + 0xb9, 0x00, 0xf9, 0x35, 0xbb, 0x45, 0xe7, 0x32, 0xe7, 0x33, 0x97, 0x46, 0xcb, 0x53, 0x8f, 0x0f, + 0x17, 0xc6, 0x7c, 0xea, 0x3d, 0xa4, 0x9e, 0xd5, 0xb6, 0x5b, 0xd4, 0xc4, 0x42, 0xf2, 0x2a, 0x8c, + 0xb2, 0xff, 0xfd, 0x8e, 0x5d, 0xa7, 0x73, 0x59, 0xc4, 0x9c, 0x78, 0x7c, 0xb8, 0x30, 0xda, 0x96, + 0x40, 0x33, 0x2a, 0x27, 0x55, 0x18, 0x59, 0x7e, 0xd4, 0x71, 0x3c, 0xea, 0xcf, 0xe5, 0xcf, 0x67, + 0x2e, 0x8d, 0x2d, 0xce, 0x5f, 0xe1, 0x9d, 0xbd, 0x22, 0x3b, 0x7b, 0x65, 0x53, 0x76, 0xb6, 0x3c, + 0xf3, 0xbb, 0x87, 0x0b, 0xcf, 0x3d, 0x3e, 0x5c, 0x18, 0xa1, 0x9c, 0xe4, 0x27, 0xff, 0x70, 0x21, + 0x63, 0x4a, 0x7a, 0xf2, 0x36, 0xe4, 0x37, 0x0f, 0x3a, 0x74, 0x6e, 0xf4, 0x7c, 0xe6, 0xd2, 0xe4, + 0xe2, 0xf3, 0x57, 0xf8, 0xf0, 0x86, 0x8d, 0x8f, 0xfe, 0x62, 0x58, 0xe5, 0xc2, 0xe3, 0xc3, 0x85, + 0x3c, 0x43, 0x31, 0x91, 0x8a, 0xbc, 0x0e, 0xc3, 0x2b, 0xae, 0x1f, 0x54, 0x2b, 0x73, 0x80, 0x4d, + 0x3e, 0xf1, 0xf8, 0x70, 0x61, 0x7a, 0xcf, 0xf5, 0x03, 0xcb, 0x69, 0xbc, 0xe6, 0xb6, 0x9c, 0x80, + 0xb6, 0x3a, 0xc1, 0x81, 0x29, 0x90, 0x8c, 0x47, 0x30, 0xa1, 0xf1, 0x23, 0x63, 0x30, 0x72, 0x6f, + 0xed, 0xce, 0xda, 0xfa, 0xd6, 0x5a, 0xf1, 0x39, 0x52, 0x80, 0xfc, 0xda, 0x7a, 0x65, 0xb9, 0x98, + 0x21, 0x23, 0x90, 0x2b, 0x6d, 0x6c, 0x14, 0xb3, 0x64, 0x1c, 0x0a, 0x95, 0xd2, 0x66, 0xa9, 0x5c, + 0xaa, 0x2d, 0x17, 0x73, 0x64, 0x06, 0xa6, 0xb6, 0xaa, 0x6b, 0x95, 0xf5, 0xad, 0x9a, 0x55, 0x59, + 0xae, 0xdd, 0xd9, 0x5c, 0xdf, 0x28, 0xe6, 0xc9, 0x24, 0xc0, 0x9d, 0x7b, 0xe5, 0x65, 0x73, 0x6d, + 0x79, 0x73, 0xb9, 0x56, 0x1c, 0x22, 0xb3, 0x50, 0x94, 0x24, 0x56, 0x6d, 0xd9, 0xbc, 0x5f, 0x5d, + 0x5a, 0x2e, 0x0e, 0xdf, 0xce, 0x17, 0x72, 0xc5, 0xbc, 0x39, 0xb2, 0x4a, 0x6d, 0x9f, 0x56, 0x2b, + 0xc6, 0xdf, 0xce, 0x41, 0xe1, 0x2e, 0x0d, 0xec, 0x86, 0x1d, 0xd8, 0xe4, 0xac, 0x36, 0x3f, 0xd8, + 0x45, 0x65, 0x62, 0x2e, 0x24, 0x27, 0x66, 0xe8, 0xf1, 0xe1, 0x42, 0xe6, 0x75, 0x75, 0x42, 0xde, + 0x82, 0xb1, 0x0a, 0xf5, 0xeb, 0x9e, 0xd3, 0x61, 0x8b, 0x66, 0x2e, 0x87, 0x68, 0xa7, 0x1f, 0x1f, + 0x2e, 0x9c, 0x68, 0x44, 0x60, 0x65, 0x40, 0x54, 0x6c, 0x52, 0x85, 0xe1, 0x55, 0x7b, 0x9b, 0x36, + 0xfd, 0xb9, 0xa1, 0xf3, 0xb9, 0x4b, 0x63, 0x8b, 0x67, 0xc4, 0x24, 0xc8, 0x06, 0x5e, 0xe1, 0xa5, + 0xcb, 0xed, 0xc0, 0x3b, 0x28, 0xcf, 0x3e, 0x3e, 0x5c, 0x28, 0x36, 0x11, 0xa0, 0x0e, 0x30, 0x47, + 0x21, 0xb5, 0x68, 0x61, 0x0c, 0x1f, 0xb9, 0x30, 0xce, 0xfd, 0xee, 0xe1, 0x42, 0x86, 0x4d, 0x98, + 0x58, 0x18, 0x11, 0x3f, 0x7d, 0x89, 0x2c, 0x42, 0xc1, 0xa4, 0x0f, 0x1d, 0x9f, 0xf5, 0xac, 0x80, + 0x3d, 0x3b, 0xf9, 0xf8, 0x70, 0x81, 0x78, 0x02, 0xa6, 0x34, 0x23, 0xc4, 0x9b, 0x7f, 0x13, 0xc6, + 0x94, 0x56, 0x93, 0x22, 0xe4, 0x1e, 0xd0, 0x03, 0x3e, 0xc2, 0x26, 0xfb, 0x93, 0xcc, 0xc2, 0xd0, + 0x43, 0xbb, 0xd9, 0x15, 0x43, 0x6a, 0xf2, 0x1f, 0x5f, 0xc9, 0x7e, 0x39, 0x73, 0x3b, 0x5f, 0x18, + 0x29, 0x16, 0xcc, 0x6c, 0xb5, 0x62, 0xfc, 0xeb, 0x79, 0x28, 0x98, 0x2e, 0xff, 0x10, 0xc9, 0x65, + 0x18, 0xaa, 0x05, 0x76, 0x20, 0xa7, 0x69, 0xe6, 0xf1, 0xe1, 0xc2, 0x14, 0xfb, 0x48, 0xa9, 0x52, + 0x3f, 0xc7, 0x60, 0xa8, 0x1b, 0x7b, 0xb6, 0x2f, 0xa7, 0x0b, 0x51, 0x3b, 0x0c, 0xa0, 0xa2, 0x22, + 0x06, 0xb9, 0x08, 0xf9, 0xbb, 0x6e, 0x83, 0x8a, 0x19, 0x23, 0x8f, 0x0f, 0x17, 0x26, 0x5b, 0x6e, + 0x43, 0x45, 0xc4, 0x72, 0xf2, 0x1a, 0x8c, 0x2e, 0x75, 0x3d, 0x8f, 0xb6, 0xd9, 0x5a, 0xcf, 0x23, + 0xf2, 0xe4, 0xe3, 0xc3, 0x05, 0xa8, 0x73, 0xa0, 0xe5, 0x34, 0xcc, 0x08, 0x81, 0x4d, 0x43, 0x2d, + 0xb0, 0xbd, 0x80, 0x36, 0xe6, 0x86, 0x06, 0x9a, 0x06, 0xf6, 0x7d, 0x4e, 0xfb, 0x9c, 0x24, 0x3e, + 0x0d, 0x82, 0x13, 0x59, 0x81, 0xb1, 0x5b, 0x9e, 0x5d, 0xa7, 0x1b, 0xd4, 0x73, 0xdc, 0x06, 0xce, + 0x6f, 0xae, 0x7c, 0xf1, 0xf1, 0xe1, 0xc2, 0xc9, 0x5d, 0x06, 0xb6, 0x3a, 0x08, 0x8f, 0xa8, 0x3f, + 0x39, 0x5c, 0x28, 0x54, 0xba, 0x1e, 0x8e, 0x9e, 0xa9, 0x92, 0x92, 0x6f, 0xb2, 0xc9, 0xf1, 0x03, + 0x1c, 0x5a, 0xda, 0x98, 0x1b, 0x39, 0xb2, 0x89, 0x86, 0x68, 0xe2, 0xc9, 0xa6, 0xed, 0x07, 0x96, + 0xc7, 0xe9, 0x62, 0xed, 0x54, 0x59, 0x92, 0x75, 0x28, 0xd4, 0xea, 0x7b, 0xb4, 0xd1, 0x6d, 0x52, + 0x5c, 0x32, 0x63, 0x8b, 0xa7, 0xc4, 0xa2, 0x96, 0xf3, 0x29, 0x8b, 0xcb, 0xf3, 0x82, 0x37, 0xf1, + 0x05, 0x44, 0x5d, 0x4f, 0x12, 0xeb, 0x2b, 0x85, 0x9f, 0xfb, 0x85, 0x85, 0xe7, 0x7e, 0xe0, 0x0f, + 0xce, 0x3f, 0x67, 0xfc, 0x87, 0x59, 0x28, 0xc6, 0x99, 0x90, 0x1d, 0x98, 0xb8, 0xd7, 0x69, 0xd8, + 0x01, 0x5d, 0x6a, 0x3a, 0xb4, 0x1d, 0xf8, 0xb8, 0x48, 0xfa, 0xf7, 0xe9, 0x45, 0x51, 0xef, 0x5c, + 0x17, 0x09, 0xad, 0x3a, 0xa7, 0x8c, 0xf5, 0x4a, 0x67, 0x1b, 0xd5, 0x53, 0x43, 0x01, 0xee, 0xe3, + 0x0a, 0x3b, 0x5e, 0x3d, 0x5c, 0xf4, 0xf7, 0xa8, 0x47, 0xb0, 0x15, 0x0b, 0xa8, 0xdd, 0xd8, 0x3e, + 0xc0, 0x95, 0x39, 0xf8, 0x02, 0x62, 0x24, 0x29, 0x0b, 0x88, 0x81, 0x8d, 0x7f, 0x9c, 0x81, 0x49, + 0x93, 0xfa, 0x6e, 0xd7, 0xab, 0xd3, 0x15, 0x6a, 0x37, 0xa8, 0xc7, 0x96, 0xff, 0x1d, 0xa7, 0xdd, + 0x10, 0xdf, 0x14, 0x2e, 0xff, 0x07, 0x4e, 0x5b, 0x15, 0xdd, 0x58, 0x4e, 0xbe, 0x00, 0x23, 0xb5, + 0xee, 0x36, 0xa2, 0x66, 0x23, 0x09, 0xe0, 0x77, 0xb7, 0xad, 0x18, 0xba, 0x44, 0x23, 0x57, 0x61, + 0xe4, 0x3e, 0xf5, 0xfc, 0x48, 0x1a, 0xe2, 0xd6, 0xf0, 0x90, 0x83, 0x54, 0x02, 0x81, 0x45, 0x6e, + 0x45, 0x12, 0x59, 0x6c, 0x6a, 0x53, 0x31, 0x39, 0x18, 0x2d, 0x95, 0x96, 0x80, 0xa8, 0x4b, 0x45, + 0x62, 0x19, 0x3f, 0x95, 0x85, 0x62, 0xc5, 0x0e, 0xec, 0x6d, 0xdb, 0x17, 0xe3, 0x79, 0xff, 0x3a, + 0x93, 0xf1, 0x4a, 0x47, 0x51, 0xc6, 0xb3, 0x96, 0x7f, 0xea, 0xee, 0xbd, 0x14, 0xef, 0xde, 0x18, + 0xdb, 0x61, 0x45, 0xf7, 0xa2, 0x4e, 0xbd, 0x73, 0x74, 0xa7, 0x8a, 0xa2, 0x53, 0x05, 0xd9, 0xa9, + 0xa8, 0x2b, 0xe4, 0x1d, 0xc8, 0xd7, 0x3a, 0xb4, 0x2e, 0x84, 0x88, 0xdc, 0x17, 0xf4, 0xce, 0x31, + 0x84, 0xfb, 0xd7, 0xcb, 0xe3, 0x82, 0x4d, 0xde, 0xef, 0xd0, 0xba, 0x89, 0x64, 0xca, 0x47, 0xf3, + 0x0f, 0x72, 0x30, 0x9b, 0x46, 0xa6, 0xf6, 0x63, 0xb8, 0x4f, 0x3f, 0x2e, 0x41, 0x81, 0x6d, 0xe1, + 0x6c, 0x5b, 0x44, 0x71, 0x31, 0x5a, 0x1e, 0x67, 0x4d, 0xde, 0x13, 0x30, 0x33, 0x2c, 0x25, 0x17, + 0x42, 0x8d, 0xa0, 0x10, 0xf1, 0x13, 0x1a, 0x81, 0xd4, 0x03, 0xd8, 0x5c, 0xcb, 0x4f, 0x18, 0x15, + 0x87, 0x68, 0x58, 0x24, 0x38, 0x9a, 0x6b, 0x4f, 0x40, 0xb4, 0x6d, 0x46, 0x6e, 0x0a, 0xcb, 0x50, + 0x90, 0xdd, 0x9a, 0x1b, 0x47, 0x46, 0xd3, 0xb1, 0x41, 0xba, 0x7f, 0x9d, 0x4f, 0x66, 0x43, 0xfc, + 0x56, 0xd9, 0x48, 0x1c, 0x72, 0x1d, 0x0a, 0x1b, 0x9e, 0xfb, 0xe8, 0xa0, 0x5a, 0xf1, 0xe7, 0x26, + 0xce, 0xe7, 0x2e, 0x8d, 0x96, 0x4f, 0x3d, 0x3e, 0x5c, 0x98, 0xe9, 0x30, 0x98, 0xe5, 0x34, 0xd4, + 0x9d, 0x36, 0x44, 0xbc, 0x9d, 0x2f, 0x64, 0x8a, 0xd9, 0xdb, 0xf9, 0x42, 0xb6, 0x98, 0xe3, 0xea, + 0xc5, 0xed, 0x7c, 0x21, 0x5f, 0x1c, 0xba, 0x9d, 0x2f, 0x0c, 0xa1, 0xc2, 0x31, 0x5a, 0x84, 0xdb, + 0xf9, 0xc2, 0x58, 0x71, 0x5c, 0xdb, 0xed, 0x91, 0x41, 0xe0, 0xd6, 0xdd, 0xa6, 0x99, 0xbb, 0x67, + 0x56, 0xcd, 0xe1, 0xa5, 0xd2, 0x12, 0xf5, 0x02, 0x33, 0x57, 0xda, 0xaa, 0x99, 0x13, 0x95, 0x83, + 0xb6, 0xdd, 0x72, 0xea, 0x7c, 0xeb, 0x34, 0x73, 0xb7, 0x96, 0x36, 0x8c, 0x12, 0x4c, 0x46, 0x7d, + 0x59, 0x75, 0xfc, 0x80, 0x5c, 0x85, 0x51, 0x09, 0x61, 0x82, 0x2e, 0x97, 0xda, 0x6b, 0x33, 0xc2, + 0x31, 0x7e, 0x27, 0x0b, 0x10, 0x95, 0x3c, 0xa3, 0xdf, 0xc2, 0x97, 0xb4, 0x6f, 0xe1, 0x44, 0xfc, + 0x5b, 0xe8, 0xf9, 0x15, 0x90, 0xf7, 0x60, 0x98, 0xa9, 0x05, 0x5d, 0xa9, 0x12, 0x9d, 0x8a, 0x93, + 0x62, 0xe1, 0xfd, 0xeb, 0xe5, 0x49, 0x41, 0x3c, 0xec, 0x23, 0xc4, 0x14, 0x64, 0xca, 0x67, 0xf4, + 0xf3, 0x23, 0xd1, 0x64, 0x88, 0x0f, 0xe8, 0x12, 0x84, 0x13, 0x2a, 0x06, 0x14, 0xbf, 0x8c, 0x8e, + 0x9c, 0xe4, 0xb0, 0x94, 0x9c, 0x06, 0x36, 0xe1, 0x62, 0x50, 0x47, 0x1e, 0x1f, 0x2e, 0xe4, 0xba, + 0x9e, 0x83, 0x8b, 0x80, 0x5c, 0x05, 0xb1, 0x0c, 0xc4, 0x00, 0xb2, 0xd5, 0x37, 0x5d, 0xb7, 0xad, + 0x3a, 0xf5, 0x82, 0x68, 0xc4, 0xe7, 0x32, 0x72, 0xb5, 0x90, 0x0e, 0xe8, 0x4b, 0x65, 0x2e, 0x8f, + 0xcb, 0xe0, 0x52, 0xea, 0xa8, 0x5c, 0xd1, 0x50, 0xb9, 0x1a, 0x79, 0x5e, 0xee, 0x4a, 0x0d, 0x5e, + 0x66, 0x25, 0x54, 0x4a, 0xbd, 0x02, 0x72, 0x1d, 0xd8, 0x0a, 0x15, 0xa3, 0x0f, 0xa2, 0x9e, 0xd2, + 0x56, 0xad, 0x7c, 0x42, 0x70, 0x9a, 0xb0, 0xf7, 0x55, 0x72, 0x86, 0x4d, 0xde, 0x02, 0xb6, 0x84, + 0xc5, 0xb8, 0x13, 0x41, 0x74, 0x6b, 0x69, 0x63, 0xa9, 0xe9, 0x76, 0x1b, 0xb5, 0xaf, 0xae, 0x46, + 0xc4, 0xbb, 0xf5, 0x8e, 0x4a, 0x7c, 0x6b, 0x69, 0x83, 0xbc, 0x05, 0x43, 0xa5, 0x6f, 0x77, 0x3d, + 0x2a, 0xf4, 0x93, 0x71, 0x59, 0x27, 0x83, 0x95, 0x4f, 0x09, 0xc2, 0x29, 0x9b, 0xfd, 0x54, 0xf5, + 0x3a, 0x2c, 0x67, 0x35, 0x6f, 0xae, 0xd6, 0x84, 0xee, 0x41, 0x62, 0xc3, 0xb2, 0xb9, 0xaa, 0x34, + 0x3b, 0xd0, 0x7a, 0xcd, 0xa8, 0xc8, 0x55, 0xc8, 0x96, 0x2a, 0x78, 0x22, 0x1a, 0x5b, 0x1c, 0x95, + 0xd5, 0x56, 0xca, 0xb3, 0x82, 0x64, 0xdc, 0x56, 0x3f, 0x83, 0x6c, 0xa9, 0x42, 0xca, 0x30, 0x74, + 0xf7, 0xa0, 0xf6, 0xd5, 0x55, 0x21, 0xcc, 0x66, 0xe4, 0xba, 0x66, 0xb0, 0x75, 0xfc, 0xec, 0xfd, + 0xa8, 0xc5, 0xad, 0x03, 0xff, 0x5b, 0x4d, 0xb5, 0xc5, 0x88, 0x46, 0x36, 0x60, 0xb4, 0xd4, 0x68, + 0x39, 0xed, 0x7b, 0x3e, 0xf5, 0xe6, 0xc6, 0x90, 0xcf, 0x5c, 0xac, 0xdd, 0x61, 0x79, 0x79, 0xee, + 0xf1, 0xe1, 0xc2, 0xac, 0xcd, 0x7e, 0x5a, 0x5d, 0x9f, 0x7a, 0x0a, 0xb7, 0x88, 0x09, 0xd9, 0x00, + 0xb8, 0xeb, 0xb6, 0x77, 0xdd, 0x52, 0xd0, 0xb4, 0xfd, 0x98, 0x78, 0x8c, 0x0a, 0x42, 0xf5, 0xe1, + 0x44, 0x8b, 0xc1, 0x2c, 0x9b, 0x01, 0x15, 0x86, 0x0a, 0x0f, 0x72, 0x13, 0x86, 0xd7, 0x3d, 0xbb, + 0xde, 0xa4, 0x73, 0x13, 0xc8, 0x6d, 0x56, 0x70, 0xe3, 0x40, 0xd9, 0xd3, 0x39, 0xc1, 0xb0, 0xe8, + 0x22, 0x58, 0x3d, 0xa6, 0x70, 0xc4, 0xf9, 0x2d, 0x20, 0xc9, 0x35, 0x99, 0x72, 0x48, 0x78, 0x55, + 0x3d, 0x24, 0x44, 0x1f, 0xfd, 0x92, 0xdb, 0x6a, 0xd9, 0xed, 0x06, 0xd2, 0xde, 0x5f, 0x54, 0xce, + 0x0e, 0xc6, 0xb7, 0x60, 0x3a, 0x31, 0x58, 0x47, 0x9c, 0xef, 0xde, 0x85, 0xa9, 0x0a, 0xdd, 0xb1, + 0xbb, 0xcd, 0x20, 0xdc, 0x49, 0xf8, 0x27, 0x8a, 0x27, 0xad, 0x06, 0x2f, 0xb2, 0xe4, 0xf6, 0x61, + 0xc6, 0x91, 0x8d, 0x77, 0x60, 0x42, 0xeb, 0x3e, 0x3b, 0x2a, 0x94, 0xba, 0x0d, 0x27, 0xc0, 0x89, + 0xcc, 0x44, 0x47, 0x05, 0x9b, 0x01, 0x71, 0xba, 0xcc, 0x08, 0xc1, 0xf8, 0xb7, 0x54, 0x6d, 0x45, + 0x48, 0x22, 0x76, 0xac, 0x16, 0xf2, 0x20, 0x13, 0xe9, 0x4e, 0x09, 0x79, 0x10, 0x4a, 0x83, 0xcb, + 0xfc, 0xdb, 0xcc, 0x26, 0xbe, 0xcd, 0x31, 0x31, 0x13, 0x39, 0x7b, 0xdf, 0xe7, 0x5f, 0x64, 0xb8, + 0x52, 0x73, 0x9f, 0x7e, 0xa5, 0xbe, 0x07, 0xe3, 0x77, 0xed, 0xb6, 0xbd, 0x4b, 0x1b, 0xac, 0x07, + 0x5c, 0xf6, 0x8c, 0x96, 0xcf, 0x3c, 0x3e, 0x5c, 0x38, 0xd5, 0xe2, 0x70, 0xec, 0xa5, 0xba, 0x88, + 0x34, 0x02, 0x72, 0x4d, 0x7e, 0xd9, 0x43, 0x29, 0x5f, 0xf6, 0x84, 0xa8, 0x7d, 0x08, 0xbf, 0x6c, + 0xf1, 0x3d, 0x1b, 0xbf, 0x39, 0x8a, 0x7d, 0x24, 0xaf, 0xc1, 0xb0, 0x49, 0x77, 0xd9, 0x56, 0x93, + 0x89, 0x26, 0xc9, 0x43, 0x88, 0x3a, 0x30, 0x1c, 0x07, 0xf5, 0x0c, 0xda, 0xf0, 0xf7, 0x9c, 0x9d, + 0x40, 0x8c, 0x4e, 0xa8, 0x67, 0x08, 0xb0, 0xa2, 0x67, 0x08, 0x88, 0x7e, 0x9c, 0xe5, 0x30, 0x26, + 0xfd, 0xcc, 0x4a, 0x4d, 0x0c, 0x9a, 0x1c, 0x61, 0xb3, 0xa2, 0x88, 0x11, 0x4f, 0xd3, 0x12, 0x18, + 0x36, 0xb9, 0x01, 0xa3, 0xa5, 0x7a, 0xdd, 0xed, 0x2a, 0x67, 0x46, 0xfe, 0xdd, 0x72, 0xa0, 0x6e, + 0x22, 0x89, 0x50, 0x49, 0x0d, 0xc6, 0x96, 0xd9, 0x41, 0xcb, 0x59, 0xb2, 0xeb, 0x7b, 0x72, 0x90, + 0xa4, 0x0c, 0x53, 0x4a, 0xa2, 0x2f, 0x97, 0x22, 0xb0, 0xce, 0x80, 0xaa, 0x91, 0x41, 0xc1, 0x25, + 0x9b, 0x30, 0x56, 0xa3, 0x75, 0x8f, 0x06, 0xb5, 0xc0, 0xf5, 0x68, 0x4c, 0x24, 0x2b, 0x25, 0xe5, + 0xe7, 0xe5, 0x59, 0xcf, 0x47, 0xa0, 0xe5, 0x33, 0xa8, 0xca, 0x55, 0x41, 0xe6, 0x4a, 0x7b, 0xcb, + 0xf5, 0x0e, 0x2a, 0x65, 0x21, 0xa6, 0xa3, 0x3d, 0x9d, 0x83, 0x55, 0xa5, 0x9d, 0x41, 0x1a, 0xdb, + 0xba, 0xd2, 0xce, 0xb1, 0x70, 0xa6, 0x2a, 0x35, 0xd4, 0xad, 0x84, 0xd0, 0x9e, 0x8a, 0x46, 0x19, + 0xc1, 0xca, 0x4c, 0x35, 0x7c, 0xd4, 0xcc, 0xb4, 0x99, 0x12, 0x58, 0xa4, 0x03, 0x44, 0xce, 0x1a, + 0x57, 0x74, 0x9b, 0xd4, 0xf7, 0x85, 0x2c, 0x3f, 0x1d, 0x9b, 0xfc, 0x08, 0xa1, 0xfc, 0x92, 0x60, + 0x7e, 0x4e, 0x2e, 0x03, 0x71, 0x4e, 0x63, 0x85, 0x4a, 0x3d, 0x29, 0xbc, 0xc9, 0x9b, 0x00, 0xcb, + 0x8f, 0x02, 0xea, 0xb5, 0xed, 0x66, 0x68, 0x07, 0x43, 0xd3, 0x0f, 0x15, 0x50, 0x7d, 0xa2, 0x15, + 0x64, 0xb2, 0x04, 0x13, 0x25, 0xdf, 0xef, 0xb6, 0xa8, 0xe9, 0x36, 0x69, 0xc9, 0x5c, 0x43, 0xb9, + 0x3f, 0x5a, 0x3e, 0xf7, 0xf8, 0x70, 0xe1, 0xb4, 0x8d, 0x05, 0x96, 0xe7, 0x36, 0xa9, 0x65, 0x7b, + 0xea, 0xea, 0xd6, 0x69, 0xc8, 0x3a, 0xc0, 0x7a, 0x87, 0xb6, 0x6b, 0xd4, 0xf6, 0xea, 0x7b, 0x31, + 0x31, 0x1f, 0x15, 0x94, 0xcf, 0x8a, 0x1e, 0xce, 0xba, 0x1d, 0xda, 0xf6, 0x11, 0xa6, 0xb6, 0x2a, + 0xc2, 0x24, 0x5b, 0x30, 0x55, 0x2d, 0xdd, 0xdd, 0x70, 0x9b, 0x4e, 0xfd, 0x40, 0x68, 0x4e, 0x93, + 0x68, 0x1d, 0x3c, 0x29, 0xb8, 0xc6, 0x4a, 0xb9, 0x78, 0x72, 0xec, 0x96, 0xd5, 0x41, 0xa8, 0x25, + 0xf4, 0xa7, 0x38, 0x17, 0xf2, 0x01, 0x5b, 0x83, 0x3e, 0x53, 0x06, 0x37, 0xed, 0x5d, 0x7f, 0x6e, + 0x4a, 0xb3, 0x76, 0x95, 0xb6, 0x6a, 0x57, 0x94, 0x52, 0xae, 0xa6, 0xcc, 0xf3, 0x85, 0x88, 0x50, + 0x2b, 0xb0, 0x77, 0x7d, 0x7d, 0x21, 0x86, 0xd8, 0xe4, 0x36, 0x40, 0xc5, 0xad, 0x77, 0x5b, 0xb4, + 0x1d, 0x54, 0xca, 0x73, 0x45, 0xfd, 0x28, 0x10, 0x16, 0x44, 0xa2, 0xad, 0xe1, 0xd6, 0xb5, 0x95, + 0xa8, 0x50, 0xcf, 0xbf, 0x0b, 0xc5, 0x78, 0x43, 0x8e, 0x69, 0xc0, 0x9a, 0x28, 0x4e, 0x2a, 0xbd, + 0x5f, 0x7e, 0xe4, 0xf8, 0x81, 0x6f, 0x7c, 0xaf, 0xf6, 0x05, 0x32, 0xe9, 0x70, 0x87, 0x1e, 0x6c, + 0x78, 0x74, 0xc7, 0x79, 0x24, 0x84, 0x19, 0x4a, 0x87, 0x07, 0xf4, 0xc0, 0xea, 0x20, 0x54, 0x95, + 0x0e, 0x21, 0x2a, 0xf9, 0x22, 0x14, 0xee, 0xdc, 0xad, 0xdd, 0xa1, 0x07, 0xd5, 0x8a, 0xd8, 0xa8, + 0x38, 0x59, 0xcb, 0xb7, 0x18, 0xa9, 0xb6, 0xd6, 0x42, 0x4c, 0xa3, 0x1c, 0x49, 0x42, 0x56, 0xf3, + 0x52, 0xb3, 0xeb, 0x07, 0xd4, 0xab, 0x56, 0xd4, 0x9a, 0xeb, 0x1c, 0x18, 0x93, 0x4b, 0x21, 0xaa, + 0xf1, 0x0f, 0xb2, 0x28, 0x05, 0xd9, 0x82, 0xaf, 0xb6, 0xfd, 0xc0, 0x6e, 0xd7, 0x69, 0xc8, 0x00, + 0x17, 0xbc, 0x23, 0xa0, 0xb1, 0x05, 0x1f, 0x21, 0xeb, 0x55, 0x67, 0x07, 0xae, 0x9a, 0x55, 0x29, + 0x2d, 0x17, 0xd5, 0x8a, 0x6a, 0x5e, 0xf5, 0x04, 0x34, 0x56, 0x65, 0x84, 0x4c, 0x2e, 0xc2, 0x48, + 0xb5, 0x74, 0xb7, 0xd4, 0x0d, 0xf6, 0x50, 0x06, 0x17, 0xb8, 0x7e, 0xce, 0x56, 0xab, 0xdd, 0x0d, + 0xf6, 0x4c, 0x59, 0x48, 0xae, 0xe2, 0xb9, 0xa7, 0x4d, 0x03, 0x6e, 0x86, 0x15, 0x9b, 0xae, 0xcf, + 0x41, 0xb1, 0x63, 0x0f, 0x03, 0x91, 0x57, 0x60, 0xe8, 0xfe, 0xc6, 0x52, 0xb5, 0x22, 0x0e, 0xce, + 0xb8, 0x13, 0x3d, 0xec, 0xd4, 0xf5, 0x96, 0x70, 0x14, 0xe3, 0xb7, 0x32, 0x91, 0x7c, 0x23, 0x17, + 0x35, 0x7d, 0x04, 0x8d, 0x2e, 0x4c, 0x1f, 0x51, 0x8d, 0x2e, 0xa8, 0x99, 0x98, 0x40, 0x96, 0xba, + 0x7e, 0xe0, 0xb6, 0x96, 0xdb, 0x8d, 0x8e, 0xeb, 0xb4, 0x03, 0xa4, 0xe2, 0xa3, 0x66, 0x3c, 0x3e, + 0x5c, 0x78, 0xbe, 0x8e, 0xa5, 0x16, 0x15, 0xc5, 0x56, 0x8c, 0x4b, 0x0a, 0xf5, 0x13, 0x0c, 0xa4, + 0xf1, 0x7b, 0x59, 0x6d, 0x5f, 0x62, 0xcd, 0x33, 0x69, 0xa7, 0xe9, 0xd4, 0xf1, 0x28, 0x7e, 0xcb, + 0x73, 0xbb, 0x9d, 0x70, 0x39, 0x60, 0xf3, 0xbc, 0xa8, 0xd4, 0xda, 0x65, 0xc5, 0x3a, 0xef, 0x14, + 0x6a, 0xf2, 0x3e, 0x8c, 0x33, 0x15, 0x41, 0xfc, 0xf4, 0xe7, 0xb2, 0x38, 0x13, 0x67, 0xd1, 0x7c, + 0xe6, 0x53, 0x2f, 0x64, 0xa3, 0xe9, 0x16, 0x2a, 0x05, 0x69, 0xc0, 0xdc, 0xa6, 0x67, 0xb7, 0x7d, + 0x27, 0x58, 0x6e, 0xd7, 0xbd, 0x03, 0x54, 0x69, 0x96, 0xdb, 0xf6, 0x76, 0x93, 0x36, 0xb0, 0xbb, + 0x85, 0xf2, 0xa5, 0xc7, 0x87, 0x0b, 0x2f, 0x06, 0x1c, 0xc7, 0xa2, 0x21, 0x92, 0x45, 0x39, 0x96, + 0xc2, 0xb9, 0x27, 0x27, 0xa6, 0x02, 0xc9, 0x61, 0xc5, 0xdb, 0x13, 0xbe, 0xbb, 0xa3, 0x0a, 0x14, + 0xce, 0x06, 0x13, 0x3e, 0x6a, 0x33, 0x55, 0x02, 0xe3, 0x4f, 0x33, 0xd1, 0xce, 0x49, 0xde, 0x86, + 0x31, 0xb1, 0xd4, 0x95, 0x75, 0x81, 0xa2, 0x4f, 0x7e, 0x17, 0xb1, 0x99, 0x55, 0xd1, 0xd9, 0x81, + 0xbd, 0xb4, 0xb4, 0xaa, 0xac, 0x0d, 0x3c, 0xb0, 0xdb, 0xf5, 0x66, 0x9c, 0x4a, 0xa2, 0xb1, 0x45, + 0xb0, 0xb9, 0x5a, 0xd3, 0x47, 0x05, 0x17, 0x41, 0xd0, 0xf4, 0x53, 0x86, 0x41, 0x41, 0x7e, 0xf2, + 0x8e, 0xff, 0xf7, 0x99, 0xb4, 0x0d, 0x9a, 0x94, 0x61, 0x62, 0xcb, 0xf5, 0x1e, 0xe0, 0xfc, 0x2a, + 0x83, 0x80, 0x33, 0xbf, 0x2f, 0x0b, 0xe2, 0x1d, 0xd2, 0x49, 0xd4, 0xb6, 0x29, 0xa3, 0xa1, 0xb7, + 0x2d, 0xc6, 0x41, 0x23, 0x60, 0xf3, 0x10, 0x72, 0x0c, 0xbf, 0x0e, 0x9c, 0x87, 0xa8, 0x09, 0xda, + 0x12, 0x56, 0xd1, 0x8d, 0xff, 0x38, 0xa3, 0x6e, 0xc4, 0x6c, 0x90, 0x2b, 0x6e, 0xcb, 0x76, 0xda, + 0x4a, 0x77, 0xf8, 0x8d, 0x10, 0x42, 0xe3, 0x2d, 0x51, 0x90, 0xc9, 0x75, 0x28, 0xf0, 0x5f, 0xa1, + 0x90, 0x44, 0x73, 0x94, 0x20, 0xd4, 0x25, 0xbc, 0x44, 0x4c, 0xcc, 0x4c, 0xee, 0xb8, 0x33, 0xf3, + 0x9b, 0x19, 0x75, 0x0f, 0xfd, 0xb4, 0xbb, 0x44, 0x6c, 0x77, 0xc8, 0x1e, 0x67, 0x77, 0x78, 0xe2, + 0x2e, 0xfc, 0x40, 0x06, 0xc6, 0x14, 0xf3, 0x02, 0xeb, 0xc3, 0x86, 0xe7, 0x7e, 0x4c, 0xeb, 0x81, + 0xde, 0x87, 0x0e, 0x07, 0xc6, 0xfa, 0x10, 0xa2, 0x3e, 0x41, 0x1f, 0x8c, 0x7f, 0x96, 0x11, 0x87, + 0x9b, 0x81, 0xc5, 0xbc, 0x2e, 0x92, 0xb3, 0xc7, 0xd9, 0xdb, 0xde, 0x87, 0x21, 0x93, 0x36, 0x1c, + 0x5f, 0x1c, 0x4c, 0xa6, 0xd5, 0x83, 0x14, 0x16, 0x44, 0x0a, 0x8f, 0xc7, 0x7e, 0xaa, 0x1b, 0x13, + 0x96, 0x33, 0x0d, 0xb4, 0xea, 0xdf, 0x6c, 0xd2, 0x47, 0x0e, 0xff, 0x18, 0xc5, 0x1e, 0x89, 0x1a, + 0xa8, 0xe3, 0x5b, 0x3b, 0xac, 0x44, 0xa8, 0xc2, 0xea, 0x87, 0xa7, 0xd1, 0x18, 0x1f, 0x00, 0x44, + 0x55, 0x92, 0x3b, 0x50, 0x14, 0xab, 0xc1, 0x69, 0xef, 0x72, 0x0d, 0x48, 0x8c, 0xc1, 0xc2, 0xe3, + 0xc3, 0x85, 0x33, 0xf5, 0xb0, 0x4c, 0xa8, 0x8b, 0x0a, 0xdf, 0x04, 0xa1, 0xf1, 0x6f, 0x67, 0x21, + 0x5b, 0xc2, 0x09, 0xb9, 0x43, 0x0f, 0x02, 0x7b, 0xfb, 0xa6, 0xd3, 0xd4, 0x3e, 0xa6, 0x07, 0x08, + 0xb5, 0x76, 0x1c, 0xcd, 0xce, 0xa0, 0x20, 0xb3, 0x8f, 0xe9, 0x8e, 0xb7, 0xfd, 0x06, 0x12, 0x2a, + 0x1f, 0xd3, 0x03, 0x6f, 0xfb, 0x8d, 0x38, 0x59, 0x88, 0x48, 0x0c, 0x18, 0xe6, 0x1f, 0x96, 0x58, + 0x83, 0xf0, 0xf8, 0x70, 0x61, 0x98, 0x7f, 0x7f, 0xa6, 0x28, 0x21, 0xa7, 0x21, 0x57, 0xdb, 0x58, + 0x13, 0x12, 0x10, 0xed, 0x79, 0x7e, 0xa7, 0x6d, 0x32, 0x18, 0xab, 0x73, 0xb5, 0x52, 0xda, 0xc0, + 0x13, 0xfc, 0x50, 0x54, 0x67, 0xb3, 0x61, 0x77, 0xe2, 0x67, 0xf8, 0x10, 0x91, 0xbc, 0x03, 0x63, + 0x77, 0x2a, 0x4b, 0x2b, 0xae, 0xcf, 0xa5, 0xd7, 0x70, 0xb4, 0xf8, 0x1f, 0x34, 0xea, 0x16, 0x9a, + 0xd0, 0xe3, 0xdb, 0x80, 0x82, 0x6f, 0xfc, 0x48, 0x16, 0xc6, 0x14, 0x03, 0x17, 0xf9, 0xa2, 0xb8, + 0xd9, 0xcc, 0x68, 0xaa, 0xbb, 0x82, 0xc1, 0x4a, 0xb9, 0x35, 0xa4, 0xe5, 0x36, 0xa8, 0xb8, 0xe7, + 0x8c, 0x2c, 0x0f, 0xd9, 0x41, 0x2c, 0x0f, 0x6f, 0x02, 0xf0, 0x35, 0x80, 0x4d, 0x56, 0xd4, 0x09, + 0xc5, 0xc1, 0x41, 0x9d, 0x97, 0x08, 0x99, 0xdc, 0x87, 0x99, 0x4d, 0xaf, 0xeb, 0x07, 0xb5, 0x03, + 0x3f, 0xa0, 0x2d, 0xc6, 0x6d, 0xc3, 0x75, 0x9b, 0x62, 0xfd, 0xbd, 0xf8, 0xf8, 0x70, 0xe1, 0x7c, + 0xc0, 0x8a, 0x2d, 0x1f, 0xcb, 0xb1, 0x01, 0x56, 0xc7, 0x75, 0x55, 0x7b, 0x44, 0x1a, 0x03, 0xc3, + 0x84, 0x71, 0xd5, 0x9a, 0xc1, 0x76, 0x16, 0x71, 0x0b, 0x24, 0x6c, 0xd4, 0xca, 0xce, 0x22, 0x5a, + 0x99, 0xbc, 0x95, 0xd2, 0x49, 0x8c, 0x2f, 0xaa, 0x96, 0xb4, 0x41, 0x3f, 0x6c, 0xe3, 0x87, 0x32, + 0x91, 0x18, 0xb9, 0x7f, 0x8d, 0xbc, 0x05, 0xc3, 0xfc, 0xd6, 0x4d, 0x5c, 0x4e, 0x9e, 0x08, 0x4f, + 0xa3, 0xea, 0x95, 0x1c, 0x37, 0x61, 0xff, 0x3e, 0xbf, 0x99, 0x7f, 0xce, 0x14, 0x24, 0xa1, 0xf5, + 0x5b, 0x37, 0x84, 0x49, 0xee, 0x68, 0xe7, 0xbd, 0x96, 0x66, 0xfd, 0x36, 0x7e, 0x3b, 0x0f, 0x93, + 0x3a, 0x9a, 0x7a, 0x35, 0x97, 0x19, 0xe8, 0x6a, 0xee, 0x7d, 0x28, 0xb0, 0xf1, 0x70, 0xea, 0x54, + 0x6a, 0x64, 0x2f, 0xe2, 0x9d, 0x80, 0x80, 0x69, 0x57, 0xce, 0xc0, 0xa7, 0x83, 0x1d, 0x4e, 0xcd, + 0x90, 0x8a, 0x2c, 0x2a, 0xf7, 0x47, 0xb9, 0x48, 0x49, 0x91, 0xf7, 0x47, 0xea, 0xf7, 0x10, 0xde, + 0x24, 0xbd, 0x0e, 0xc3, 0x4c, 0x31, 0x0f, 0x6d, 0x27, 0xd8, 0x4a, 0xa6, 0xb3, 0xc7, 0x7c, 0x4b, + 0x38, 0x12, 0xd9, 0x82, 0xc2, 0xaa, 0xed, 0x07, 0x35, 0x4a, 0xdb, 0x03, 0x5c, 0xba, 0x2f, 0x88, + 0xa1, 0x9a, 0xc1, 0x1b, 0x6d, 0x9f, 0xd2, 0x76, 0xec, 0xd6, 0x34, 0x64, 0x46, 0x3e, 0x02, 0x58, + 0x72, 0xdb, 0x81, 0xe7, 0x36, 0x57, 0xdd, 0xdd, 0xb9, 0x61, 0x3c, 0xb4, 0x3e, 0x1f, 0x9b, 0x80, + 0x08, 0x81, 0x9f, 0x5b, 0x43, 0xcb, 0x4c, 0x9d, 0x17, 0x58, 0x4d, 0x77, 0x57, 0xfd, 0x0e, 0x22, + 0x7c, 0x72, 0x13, 0x8a, 0xd2, 0x22, 0x70, 0xaf, 0xb3, 0xeb, 0xe1, 0x02, 0x19, 0x89, 0x34, 0x0f, + 0xfa, 0x28, 0xb0, 0xba, 0x02, 0xae, 0x4a, 0xca, 0x38, 0x0d, 0xf9, 0x3a, 0x9c, 0x8a, 0xc3, 0xe4, + 0x2c, 0x17, 0x22, 0x9d, 0x5c, 0x65, 0x97, 0xb2, 0xee, 0x7b, 0xb1, 0x30, 0x3e, 0xc9, 0xc2, 0xa9, + 0x1e, 0x9d, 0x65, 0xdf, 0x03, 0x6e, 0xd7, 0xca, 0xf7, 0x10, 0xdb, 0xa5, 0xb9, 0xb3, 0xd0, 0x79, + 0xc8, 0x8a, 0x0d, 0x2e, 0x5f, 0x2e, 0x3e, 0x3e, 0x5c, 0x18, 0xd7, 0xe6, 0x31, 0x5b, 0xad, 0x90, + 0xdb, 0x90, 0x67, 0x53, 0x34, 0xc0, 0x9d, 0xb7, 0x34, 0x06, 0x4d, 0x06, 0x8e, 0xba, 0x7c, 0x70, + 0xea, 0x90, 0x07, 0xf9, 0x22, 0xe4, 0x36, 0x37, 0x57, 0x71, 0xed, 0xe4, 0xb0, 0xef, 0x13, 0x41, + 0xd0, 0xd4, 0x96, 0xea, 0x04, 0xa3, 0xbd, 0x12, 0xba, 0x48, 0x30, 0x74, 0xf2, 0xb5, 0x98, 0x2f, + 0xce, 0x2b, 0xfd, 0x27, 0x7a, 0x70, 0xd7, 0x9c, 0x27, 0xf0, 0x88, 0x31, 0xfe, 0x66, 0x36, 0xfa, + 0x86, 0x6f, 0x3a, 0xcd, 0x80, 0x7a, 0x64, 0x9e, 0x7f, 0x92, 0x91, 0x72, 0x66, 0x86, 0xbf, 0xc9, + 0x5c, 0xf4, 0x7d, 0x73, 0x56, 0xe1, 0x87, 0xfc, 0x8a, 0xf2, 0x21, 0xe7, 0xf0, 0x43, 0x9e, 0xec, + 0xf9, 0xc9, 0xbe, 0x92, 0xb2, 0x2e, 0xf1, 0x43, 0x4c, 0x59, 0x7b, 0x2f, 0xc2, 0xc4, 0x9a, 0xbb, + 0xfc, 0x28, 0x08, 0x11, 0xd9, 0x07, 0x58, 0x30, 0x75, 0x20, 0xe3, 0xb8, 0xde, 0x6c, 0x50, 0x6f, + 0x73, 0xcf, 0x6e, 0x6b, 0x97, 0xce, 0x66, 0x02, 0xce, 0x70, 0xd7, 0xe8, 0xbe, 0x8e, 0x3b, 0xc2, + 0x71, 0xe3, 0x70, 0xe3, 0x07, 0xb3, 0x72, 0x30, 0xee, 0x2f, 0x3e, 0xa3, 0x97, 0x9b, 0x6f, 0x68, + 0x97, 0x9b, 0x33, 0xa1, 0x59, 0x36, 0xbc, 0xa9, 0x5f, 0x3c, 0xe2, 0x82, 0xff, 0x7f, 0x18, 0x82, + 0x71, 0x15, 0x9d, 0x8d, 0x43, 0xa9, 0xd1, 0xf0, 0xd4, 0x71, 0xb0, 0x1b, 0x0d, 0xcf, 0x44, 0xa8, + 0x76, 0x9f, 0x9f, 0xeb, 0x7b, 0x9f, 0xff, 0x0d, 0x18, 0x5d, 0x6a, 0x35, 0xb4, 0x5b, 0x46, 0x23, + 0xa5, 0x79, 0x57, 0x42, 0x24, 0xfe, 0x2d, 0x84, 0xd6, 0xc6, 0x7a, 0xab, 0x91, 0xbc, 0x5b, 0x8c, + 0x58, 0x6a, 0xae, 0x00, 0x43, 0x4f, 0xe2, 0x0a, 0x70, 0x03, 0x46, 0xef, 0xf9, 0x74, 0xb3, 0xdb, + 0x6e, 0xd3, 0x26, 0x2e, 0xab, 0x02, 0xd7, 0xf5, 0xbb, 0x3e, 0xb5, 0x02, 0x84, 0xaa, 0x0d, 0x08, + 0x51, 0xd5, 0x09, 0x1e, 0xe9, 0x33, 0xc1, 0xd7, 0xa1, 0xb0, 0x41, 0xa9, 0x87, 0x63, 0x3a, 0x16, + 0xa9, 0x74, 0x1d, 0x4a, 0x3d, 0x8b, 0x0d, 0xac, 0xe6, 0x22, 0x20, 0x10, 0x35, 0xbf, 0x82, 0xf1, + 0x01, 0xfd, 0x0a, 0xc8, 0x0b, 0x30, 0xde, 0xe9, 0x6e, 0x37, 0x9d, 0x3a, 0xf2, 0x15, 0x0e, 0x09, + 0xe6, 0x18, 0x87, 0x31, 0xb6, 0x3e, 0xf9, 0x1a, 0x4c, 0xe0, 0x19, 0x27, 0x5c, 0x72, 0x93, 0xda, + 0x75, 0x9c, 0x56, 0xc6, 0x35, 0x9d, 0x3a, 0x03, 0x59, 0x29, 0x7e, 0x33, 0x3a, 0xa3, 0xf9, 0x1a, + 0x4c, 0xea, 0x33, 0xf9, 0x14, 0x6e, 0xe5, 0x42, 0x1f, 0x89, 0x42, 0x71, 0xf4, 0x76, 0xbe, 0x00, + 0xc5, 0x31, 0xee, 0x1d, 0x61, 0xc2, 0x46, 0xd8, 0x27, 0x93, 0xdc, 0xe9, 0x6e, 0x53, 0xaf, 0x4d, + 0x03, 0xea, 0x8b, 0x43, 0x80, 0x6f, 0xe6, 0x4b, 0x9d, 0x8e, 0x6f, 0xfc, 0x5a, 0x16, 0x46, 0x4a, + 0x5b, 0xb5, 0x6a, 0x7b, 0xc7, 0xc5, 0xbb, 0xb5, 0xf0, 0x4a, 0x45, 0xbd, 0x5b, 0x0b, 0xaf, 0x54, + 0xd4, 0x8b, 0x94, 0xab, 0x29, 0xc7, 0x38, 0x74, 0xbf, 0x55, 0x8e, 0x71, 0xda, 0x01, 0x34, 0xba, + 0x5d, 0xca, 0x0d, 0x70, 0xbb, 0x14, 0x1a, 0x00, 0xf3, 0x47, 0x1a, 0x00, 0xc9, 0x5b, 0x30, 0x56, + 0x6d, 0x07, 0x74, 0xd7, 0x8b, 0x56, 0x7a, 0x78, 0xa4, 0x0c, 0xc1, 0xaa, 0x6a, 0xaf, 0x60, 0xb3, + 0x65, 0xc4, 0x8d, 0x8e, 0xa1, 0xb1, 0x11, 0x97, 0x11, 0xb7, 0x4d, 0xc6, 0xec, 0x01, 0x12, 0xd1, + 0xa8, 0xc4, 0xd6, 0x88, 0xbc, 0xc1, 0xe7, 0xca, 0xe7, 0x64, 0x64, 0x75, 0x67, 0x03, 0x5b, 0x9e, + 0x4e, 0xbf, 0xc1, 0x37, 0xbe, 0x93, 0x85, 0xb1, 0x52, 0xa7, 0xf3, 0x8c, 0xfb, 0x51, 0x7d, 0x59, + 0x13, 0xaf, 0xf2, 0x2c, 0x14, 0xf6, 0x6b, 0x20, 0x17, 0xaa, 0x5f, 0xce, 0xc2, 0x54, 0x8c, 0x42, + 0x6d, 0x7d, 0x66, 0x40, 0xef, 0xa9, 0xec, 0x80, 0xde, 0x53, 0xb9, 0xc1, 0xbc, 0xa7, 0xf2, 0x4f, + 0x22, 0x32, 0x5f, 0x86, 0x5c, 0xa9, 0xd3, 0x89, 0xdf, 0xc2, 0x76, 0x3a, 0xf7, 0xaf, 0xf3, 0xf3, + 0xac, 0xdd, 0xe9, 0x98, 0x0c, 0x43, 0x93, 0x63, 0xc3, 0x03, 0xca, 0x31, 0xe3, 0x75, 0x18, 0x45, + 0x5e, 0xe8, 0xb3, 0x74, 0x1e, 0xf0, 0x63, 0x16, 0xee, 0x4a, 0x5a, 0x5d, 0xe2, 0x33, 0xff, 0xbf, + 0x33, 0x30, 0x84, 0xbf, 0x9f, 0xd1, 0x35, 0xb6, 0xa8, 0xad, 0xb1, 0xa2, 0xb2, 0xc6, 0x06, 0x59, + 0x5d, 0xff, 0x7b, 0x1e, 0x47, 0x4b, 0xac, 0x2b, 0xe1, 0x2a, 0x94, 0x49, 0x71, 0x15, 0x7a, 0x13, + 0x14, 0xa9, 0xa9, 0x5a, 0x8b, 0x94, 0x3d, 0x43, 0x3d, 0x69, 0x44, 0xc8, 0xe4, 0x41, 0xdc, 0x69, + 0x28, 0x87, 0x93, 0x71, 0x21, 0xde, 0xd4, 0xa7, 0xe2, 0x2f, 0xb4, 0x02, 0xa4, 0xda, 0xf6, 0x69, + 0xbd, 0xeb, 0xd1, 0xda, 0x03, 0xa7, 0x73, 0x9f, 0x7a, 0xce, 0xce, 0x81, 0x38, 0xdd, 0xe3, 0xbe, + 0xec, 0x88, 0x52, 0xcb, 0x7f, 0xe0, 0x74, 0xd8, 0x51, 0xc4, 0xd9, 0x39, 0x30, 0x53, 0x68, 0xc8, + 0x7b, 0x30, 0x62, 0xd2, 0x7d, 0xcf, 0x09, 0xe4, 0x55, 0xf8, 0x64, 0x78, 0x70, 0x46, 0x28, 0x3f, + 0x18, 0x7a, 0xfc, 0x87, 0x3a, 0xff, 0xa2, 0x9c, 0x2c, 0x72, 0xc1, 0xc7, 0xaf, 0xbc, 0x27, 0xa2, + 0xde, 0x96, 0xb6, 0x6a, 0xbd, 0xe4, 0x1e, 0xb9, 0x0c, 0x43, 0x28, 0x3d, 0x85, 0x4e, 0x80, 0x2e, + 0xe4, 0xb8, 0x87, 0xaa, 0xa2, 0x1d, 0x31, 0xc8, 0xf3, 0x00, 0xe1, 0x0d, 0x84, 0x3f, 0x57, 0xc0, + 0xdd, 0x5a, 0x81, 0xc4, 0x45, 0xff, 0xe8, 0x71, 0x44, 0xff, 0x67, 0xe7, 0x29, 0xf3, 0xcb, 0x59, + 0xb8, 0x10, 0x8a, 0xb3, 0x75, 0xaf, 0x56, 0xba, 0xbb, 0x5a, 0x6d, 0x6c, 0x08, 0xed, 0x7f, 0xc3, + 0x73, 0x1f, 0x3a, 0xec, 0xf4, 0x77, 0xed, 0x88, 0x8f, 0x71, 0x95, 0xaf, 0x5a, 0x6e, 0x3a, 0xcc, + 0x6a, 0x3e, 0x05, 0xca, 0xae, 0x21, 0xdc, 0x1e, 0x3a, 0x9d, 0x84, 0x25, 0x71, 0xe5, 0x39, 0x33, + 0x62, 0x40, 0x7e, 0x28, 0x03, 0x27, 0xd3, 0x1b, 0x22, 0x4e, 0x84, 0x0b, 0x52, 0xf3, 0xec, 0xd1, + 0xda, 0xf2, 0xcb, 0x8f, 0x0f, 0x17, 0x2e, 0xf8, 0x76, 0xab, 0x69, 0x39, 0x0d, 0x5e, 0x9b, 0x53, + 0xa7, 0x56, 0x47, 0x20, 0x68, 0xf5, 0xf6, 0xa8, 0xe9, 0x2b, 0x20, 0xbf, 0xc9, 0xb9, 0x4c, 0x19, + 0xa0, 0x20, 0xad, 0x33, 0xc6, 0xdf, 0xcf, 0x80, 0xb2, 0xa2, 0x0a, 0x26, 0x6d, 0x38, 0x1e, 0xad, + 0x07, 0x28, 0xd1, 0xc2, 0x88, 0x08, 0x0e, 0x8b, 0xb9, 0x90, 0x20, 0x8c, 0xbc, 0x0b, 0x23, 0xdc, + 0x96, 0xc3, 0x6d, 0x28, 0xd1, 0x4a, 0x14, 0x76, 0x1f, 0x1e, 0x3a, 0xc3, 0x31, 0xd4, 0x55, 0x2c, + 0x88, 0x98, 0x7e, 0x7b, 0x7b, 0x6b, 0x73, 0xa9, 0x69, 0x3b, 0x2d, 0x5f, 0xc8, 0x31, 0x1c, 0xd6, + 0x8f, 0xf7, 0x03, 0xab, 0x8e, 0x50, 0x55, 0xbf, 0x0d, 0x51, 0x8d, 0x5b, 0xd2, 0xec, 0x74, 0x84, + 0x1f, 0xd4, 0x02, 0x0c, 0xdd, 0x8f, 0x8e, 0x9f, 0xe5, 0xd1, 0xc7, 0x87, 0x0b, 0x7c, 0xb9, 0x98, + 0x1c, 0x6e, 0xfc, 0xd5, 0x0c, 0x4c, 0xea, 0xeb, 0x89, 0x5c, 0x81, 0x61, 0x11, 0x8d, 0x90, 0xc1, + 0x63, 0x36, 0x1b, 0x85, 0x61, 0x1e, 0x87, 0xa0, 0x45, 0x1f, 0x08, 0x2c, 0x26, 0x89, 0x05, 0x07, + 0x61, 0x47, 0x42, 0x49, 0x5c, 0xe7, 0x20, 0x53, 0x96, 0x11, 0x83, 0xa9, 0x61, 0x7e, 0xb7, 0x19, + 0xa8, 0xd6, 0x57, 0x0f, 0x21, 0xa6, 0x28, 0x31, 0x96, 0x60, 0x98, 0x7f, 0xc2, 0x31, 0xff, 0x8b, + 0xcc, 0x31, 0xfc, 0x2f, 0x8c, 0xc3, 0x0c, 0x40, 0xad, 0xb6, 0x72, 0x87, 0x1e, 0x6c, 0xd8, 0x8e, + 0x87, 0xd7, 0x05, 0x28, 0x2e, 0xef, 0x88, 0x8f, 0x6b, 0x5c, 0x5c, 0x17, 0x70, 0xd1, 0xfa, 0x80, + 0x1e, 0x68, 0xd7, 0x05, 0x12, 0x15, 0x65, 0xb2, 0xe7, 0x3c, 0xb4, 0x03, 0xca, 0x08, 0xb3, 0x48, + 0xc8, 0x65, 0x32, 0x87, 0xc6, 0x28, 0x15, 0x64, 0xf2, 0x11, 0x4c, 0x46, 0xbf, 0xc2, 0x4b, 0x8f, + 0xc9, 0xf0, 0x03, 0xd6, 0x0b, 0xcb, 0xcf, 0x3f, 0x3e, 0x5c, 0x98, 0x57, 0xb8, 0xc6, 0xaf, 0x43, + 0x62, 0xcc, 0x8c, 0x5f, 0xcc, 0xe0, 0x55, 0x9f, 0xec, 0xe0, 0x45, 0xc8, 0x87, 0x5e, 0x65, 0xe3, + 0xdc, 0x52, 0x13, 0x33, 0xec, 0x62, 0x39, 0xb9, 0x00, 0xb9, 0xa8, 0x27, 0x28, 0x22, 0xf5, 0x1e, + 0xb0, 0x52, 0x72, 0x0b, 0x46, 0x06, 0x6a, 0x33, 0x7e, 0x1a, 0x29, 0x6d, 0x95, 0xd4, 0x38, 0x0b, + 0xb7, 0xb7, 0x36, 0x3f, 0xbf, 0xb3, 0xf0, 0x13, 0x59, 0x98, 0x62, 0xe3, 0x5a, 0xea, 0x06, 0x7b, + 0xae, 0xe7, 0x04, 0x07, 0xcf, 0xac, 0x9d, 0xe2, 0x6d, 0x4d, 0xc9, 0x99, 0x97, 0xbb, 0x8c, 0xda, + 0xb7, 0x81, 0xcc, 0x15, 0xff, 0xe5, 0x10, 0xcc, 0xa4, 0x50, 0x91, 0xd7, 0x34, 0x53, 0xe2, 0x9c, + 0x8c, 0x36, 0xfc, 0xe4, 0x70, 0x61, 0x5c, 0xa2, 0x6f, 0x46, 0xd1, 0x87, 0x8b, 0xfa, 0xbd, 0x39, + 0x1f, 0x29, 0xb4, 0x2c, 0xaa, 0xf7, 0xe6, 0xfa, 0x6d, 0xf9, 0x65, 0x18, 0x32, 0xdd, 0x26, 0x95, + 0x4e, 0x1e, 0xb8, 0xb1, 0x7b, 0x0c, 0xa0, 0xdd, 0x8d, 0x31, 0x00, 0x59, 0x81, 0x11, 0xf6, 0xc7, + 0x5d, 0xbb, 0x23, 0xac, 0xbe, 0x24, 0x54, 0xb3, 0x11, 0xda, 0x71, 0xda, 0xbb, 0xaa, 0xa6, 0xdd, + 0xa4, 0x56, 0xcb, 0xee, 0x68, 0x1a, 0x08, 0x47, 0xd4, 0x34, 0xf6, 0x42, 0x6f, 0x8d, 0x3d, 0x73, + 0xa4, 0xc6, 0xbe, 0x03, 0x50, 0x73, 0x76, 0xdb, 0x4e, 0x7b, 0xb7, 0xd4, 0xdc, 0x15, 0x31, 0x9b, + 0x97, 0x7b, 0xcf, 0xc2, 0x95, 0x08, 0x19, 0x17, 0xee, 0x19, 0xbc, 0x9a, 0xe1, 0x30, 0xcb, 0x6e, + 0xee, 0x6a, 0xbe, 0xe5, 0x0a, 0x67, 0xb2, 0x06, 0x50, 0xaa, 0x07, 0xce, 0x43, 0xb6, 0x84, 0x7d, + 0xe1, 0x8d, 0x2c, 0x9b, 0xbc, 0x54, 0xba, 0x43, 0x0f, 0x6a, 0x34, 0x88, 0x8c, 0xdc, 0x36, 0xa2, + 0xb2, 0x2f, 0x41, 0x73, 0x1c, 0x8e, 0x38, 0x90, 0x0e, 0x9c, 0x28, 0x35, 0x1a, 0x0e, 0xeb, 0x83, + 0xdd, 0xc4, 0x5b, 0x1b, 0xda, 0x40, 0xd6, 0xe3, 0xe9, 0xac, 0x2f, 0x0b, 0xd6, 0x2f, 0xd8, 0x21, + 0x95, 0x15, 0x70, 0xb2, 0x78, 0x35, 0xe9, 0x8c, 0x8d, 0x1a, 0x4c, 0xea, 0x9d, 0xd7, 0x63, 0x4d, + 0xc7, 0xa1, 0x60, 0xd6, 0x4a, 0x56, 0x6d, 0xa5, 0x74, 0xad, 0x98, 0x21, 0x45, 0x18, 0x17, 0xbf, + 0x16, 0xad, 0xc5, 0x37, 0x6e, 0x14, 0xb3, 0x1a, 0xe4, 0x8d, 0x6b, 0x8b, 0xc5, 0xdc, 0x7c, 0x76, + 0x2e, 0x13, 0x0b, 0xf3, 0x18, 0x29, 0x16, 0xb8, 0x39, 0xc3, 0xf8, 0x95, 0x0c, 0x14, 0x64, 0xdb, + 0xc9, 0x0d, 0xc8, 0xd5, 0x6a, 0x2b, 0xb1, 0xc0, 0x8c, 0x68, 0x97, 0xe1, 0xf2, 0xd4, 0xf7, 0x55, + 0xef, 0x3b, 0x46, 0xc0, 0xe8, 0x36, 0x57, 0x6b, 0x42, 0x39, 0x90, 0x74, 0x91, 0xf0, 0xe6, 0x74, + 0x29, 0xde, 0xea, 0x37, 0x20, 0x77, 0x7b, 0x6b, 0x53, 0x28, 0xf3, 0x92, 0x2e, 0x92, 0xa7, 0x9c, + 0xee, 0xe3, 0x7d, 0x55, 0xca, 0x33, 0x02, 0xc3, 0x84, 0x31, 0x65, 0x21, 0xf3, 0x4d, 0xb7, 0xe5, + 0x86, 0x01, 0x96, 0x62, 0xd3, 0x65, 0x10, 0x53, 0x94, 0x30, 0x1d, 0x61, 0xd5, 0xad, 0xdb, 0x4d, + 0xb1, 0x7b, 0xa3, 0x8e, 0xd0, 0x64, 0x00, 0x93, 0xc3, 0x8d, 0xdf, 0xca, 0x40, 0x11, 0x35, 0x29, + 0xf4, 0x9e, 0x73, 0x1f, 0xd0, 0xf6, 0xfd, 0x6b, 0xe4, 0x75, 0xf9, 0xc9, 0x65, 0xc2, 0xa3, 0xe3, + 0x10, 0x7e, 0x72, 0x31, 0xdb, 0xb3, 0xf8, 0xec, 0x94, 0x18, 0xd6, 0xec, 0xe0, 0xb1, 0x6f, 0x47, + 0xc4, 0xb0, 0x2e, 0xc0, 0x10, 0x36, 0x47, 0x08, 0x47, 0x6c, 0x79, 0xc0, 0x00, 0x26, 0x87, 0x2b, + 0xb2, 0xe9, 0xa7, 0xb2, 0x89, 0x3e, 0x2c, 0x7e, 0xae, 0xe2, 0xc7, 0xf4, 0xce, 0x0d, 0x24, 0xaf, + 0x3f, 0x80, 0xd9, 0xf8, 0x90, 0xe0, 0xb1, 0xbe, 0x04, 0x53, 0x3a, 0x5c, 0x9e, 0xf0, 0x4f, 0xa5, + 0xd6, 0x75, 0x7f, 0xd1, 0x8c, 0xe3, 0x1b, 0xff, 0x4b, 0x06, 0x46, 0xf1, 0x4f, 0xb3, 0xdb, 0x44, + 0x67, 0x88, 0xd2, 0x56, 0x4d, 0x98, 0xf0, 0x54, 0x65, 0xce, 0xde, 0xf7, 0x2d, 0x61, 0xe5, 0xd3, + 0x64, 0x4c, 0x88, 0x2c, 0x48, 0xb9, 0x6d, 0x4e, 0xde, 0x53, 0x86, 0xa4, 0xdc, 0x88, 0xe7, 0xc7, + 0x48, 0x05, 0x32, 0xba, 0x50, 0x6d, 0xd5, 0xd8, 0xf2, 0x53, 0x6f, 0x27, 0x91, 0xce, 0x6d, 0xea, + 0x2e, 0x54, 0x1c, 0x0d, 0x2f, 0x27, 0xb7, 0x6a, 0x25, 0x73, 0x4d, 0xbb, 0x9c, 0x64, 0x6d, 0xd4, + 0x5c, 0x75, 0x05, 0x92, 0xf1, 0x0f, 0x47, 0xe3, 0x03, 0x28, 0x36, 0xbc, 0x63, 0x7e, 0x1b, 0x6f, + 0xc1, 0x50, 0xa9, 0xd9, 0x74, 0xf7, 0x85, 0x94, 0x90, 0x56, 0x86, 0x70, 0xfc, 0xf8, 0x7e, 0x66, + 0x33, 0x14, 0x2d, 0x26, 0x86, 0x01, 0xc8, 0x12, 0x8c, 0x96, 0xb6, 0x6a, 0xd5, 0x6a, 0x65, 0x73, + 0x93, 0xfb, 0xff, 0xe7, 0xca, 0x2f, 0xc9, 0xf1, 0x71, 0x9c, 0x86, 0x15, 0xbf, 0x1f, 0x8b, 0xf4, + 0xf7, 0x88, 0x8e, 0xbc, 0x03, 0x70, 0xdb, 0x75, 0xda, 0x77, 0x69, 0xb0, 0xe7, 0x36, 0x44, 0xe7, + 0xcf, 0x3d, 0x3e, 0x5c, 0x18, 0xfb, 0xd8, 0x75, 0xda, 0x56, 0x0b, 0xc1, 0xac, 0xed, 0x11, 0x92, + 0xa9, 0xfc, 0xcd, 0x46, 0xba, 0xec, 0x72, 0x07, 0x87, 0xa1, 0x68, 0xa4, 0xb7, 0xdd, 0x84, 0x6f, + 0x83, 0x44, 0x23, 0x2d, 0x98, 0xaa, 0x75, 0x77, 0x77, 0x29, 0x93, 0xec, 0xc2, 0x6e, 0x31, 0x2c, + 0xce, 0xb8, 0x61, 0xd6, 0x05, 0x7e, 0x1e, 0x61, 0xa7, 0x14, 0xbf, 0xfc, 0x1a, 0x5b, 0xc8, 0xdf, + 0x3d, 0x5c, 0x10, 0xf7, 0x6e, 0x4c, 0x55, 0xf3, 0x25, 0x7d, 0xd2, 0x6a, 0x11, 0xe7, 0x4d, 0xd6, + 0x61, 0xf8, 0x96, 0x13, 0xac, 0x74, 0xb7, 0x85, 0x3f, 0xfb, 0x0b, 0x7d, 0x3e, 0x1a, 0x8e, 0xc8, + 0x0d, 0xbf, 0xbb, 0x4e, 0xb0, 0xd7, 0x55, 0x3d, 0x8a, 0x05, 0x1b, 0xb2, 0x05, 0x85, 0x25, 0xc7, + 0xab, 0x37, 0xe9, 0x52, 0x55, 0xec, 0xfd, 0x17, 0xfa, 0xb0, 0x94, 0xa8, 0x7c, 0x5c, 0xea, 0xf8, + 0xab, 0xee, 0xa8, 0xba, 0x80, 0xc4, 0x20, 0x7f, 0x3d, 0x03, 0x67, 0xc2, 0xd6, 0x97, 0x76, 0x69, + 0x3b, 0xb8, 0x6b, 0x07, 0xf5, 0x3d, 0xea, 0x89, 0x51, 0x1a, 0xed, 0x37, 0x4a, 0x5f, 0x49, 0x8c, + 0xd2, 0xa5, 0x68, 0x94, 0x6c, 0xc6, 0xcc, 0x6a, 0x71, 0x6e, 0xc9, 0x31, 0xeb, 0x57, 0x2b, 0xb1, + 0x00, 0x22, 0x4b, 0xbe, 0x88, 0x87, 0x7a, 0xa9, 0x4f, 0x87, 0x23, 0x64, 0xe1, 0xc7, 0x1c, 0xfe, + 0xd6, 0xfc, 0x79, 0x42, 0x28, 0xb9, 0x23, 0x83, 0x47, 0xb8, 0x56, 0x72, 0xbe, 0x0f, 0x6f, 0x1e, + 0x50, 0x32, 0xd3, 0x27, 0x4c, 0x8c, 0xcf, 0xf6, 0xaa, 0xbd, 0x2d, 0x14, 0x91, 0x23, 0x66, 0x7b, + 0xd5, 0x8e, 0x66, 0xbb, 0x69, 0xc7, 0x67, 0x7b, 0xd5, 0xde, 0x26, 0x4b, 0x3c, 0xe2, 0x8d, 0x87, + 0x47, 0x3d, 0xdf, 0x8f, 0xdb, 0xd2, 0x06, 0xdf, 0x99, 0x53, 0x22, 0xdf, 0x3e, 0x84, 0xd1, 0x5a, + 0xc7, 0xae, 0xd3, 0xa6, 0xb3, 0x13, 0x88, 0xab, 0x9d, 0x17, 0xfb, 0xb0, 0x0a, 0x71, 0xc5, 0xb5, + 0x80, 0xfc, 0xa9, 0x1e, 0x93, 0x42, 0x1c, 0xd6, 0xc2, 0xcd, 0x8d, 0xbb, 0x73, 0x53, 0x47, 0xb6, + 0x70, 0x73, 0xe3, 0xae, 0xd0, 0x39, 0x3a, 0x2d, 0x4d, 0xe7, 0xd8, 0xb8, 0x6b, 0xfc, 0x7a, 0x0e, + 0x4e, 0xf5, 0xa0, 0x21, 0x6b, 0x52, 0x46, 0x65, 0x34, 0xf3, 0x62, 0x0f, 0xf4, 0x2b, 0x47, 0x8a, + 0xad, 0x55, 0x28, 0x2e, 0xdf, 0x41, 0xe5, 0x96, 0xfd, 0xa4, 0x8d, 0xa5, 0x92, 0x94, 0xee, 0xe7, + 0x1f, 0x1f, 0x2e, 0x9c, 0xa5, 0x0f, 0xd0, 0x35, 0xc8, 0xe6, 0x85, 0x56, 0x5d, 0x0b, 0x5e, 0x4b, + 0x50, 0xce, 0xff, 0x60, 0x16, 0xf2, 0xb8, 0xd3, 0xc4, 0x52, 0x76, 0x64, 0x8e, 0x95, 0xb2, 0xe3, + 0x7d, 0x18, 0x5f, 0xbe, 0xc3, 0x8f, 0x9e, 0x2b, 0xb6, 0xbf, 0x27, 0xe4, 0x20, 0xde, 0xb4, 0xd1, + 0x07, 0x96, 0x38, 0xa9, 0xee, 0xd9, 0x9a, 0x92, 0xa7, 0x51, 0x90, 0x7b, 0x30, 0xc3, 0xdb, 0xe6, + 0xec, 0x38, 0x75, 0x1e, 0xf9, 0xef, 0xd8, 0x4d, 0x21, 0x14, 0x2f, 0x3c, 0x3e, 0x5c, 0x58, 0xa0, + 0x0f, 0xd0, 0xe9, 0x49, 0x94, 0x5b, 0x3e, 0x22, 0xa8, 0xde, 0x4f, 0x29, 0xf4, 0x6a, 0x38, 0xb2, + 0x39, 0x8a, 0x15, 0xb2, 0xda, 0x58, 0xdd, 0x0c, 0x97, 0x23, 0x19, 0x7f, 0x7f, 0x08, 0xe6, 0x7b, + 0xcb, 0x33, 0xf2, 0x55, 0x7d, 0x02, 0x2f, 0x1e, 0x29, 0x01, 0x8f, 0x9e, 0xc3, 0xaf, 0xc1, 0xec, + 0x72, 0x3b, 0xa0, 0x5e, 0xc7, 0x73, 0x64, 0x00, 0xfa, 0x8a, 0xeb, 0x4b, 0x27, 0x33, 0xf4, 0xf6, + 0xa2, 0x61, 0xb9, 0xb0, 0x12, 0xa2, 0xcb, 0x9b, 0xc2, 0x2a, 0x95, 0x03, 0x59, 0x86, 0x49, 0x05, + 0xde, 0xec, 0xee, 0x8a, 0x1d, 0x1c, 0x3d, 0x18, 0x55, 0x9e, 0xcd, 0xae, 0xea, 0x81, 0x13, 0x23, + 0x9a, 0xff, 0xc5, 0x9c, 0x58, 0x16, 0x17, 0x20, 0x57, 0xeb, 0x6e, 0x8b, 0xe5, 0xc0, 0x55, 0x75, + 0x4d, 0xac, 0xb3, 0x52, 0xf2, 0x65, 0x00, 0x93, 0x76, 0x5c, 0xdf, 0x09, 0x5c, 0xef, 0x40, 0x8d, + 0x63, 0xf0, 0x42, 0xa8, 0xee, 0xb1, 0x29, 0xa1, 0x64, 0x05, 0xa6, 0xa2, 0x5f, 0xeb, 0xfb, 0x6d, + 0x61, 0xda, 0x1c, 0xe5, 0x36, 0x85, 0x88, 0xdc, 0x72, 0x59, 0x99, 0xba, 0x51, 0xc5, 0xc8, 0xc8, + 0x22, 0x14, 0xb6, 0x5c, 0xef, 0xc1, 0x0e, 0x9b, 0xa8, 0x7c, 0xb4, 0x95, 0xee, 0x0b, 0x98, 0xba, + 0x65, 0x48, 0x3c, 0xb6, 0xe6, 0x97, 0xdb, 0x0f, 0x1d, 0xcf, 0x6d, 0xb7, 0x68, 0x3b, 0x50, 0x6f, + 0x21, 0x69, 0x04, 0xd6, 0x22, 0xc8, 0x22, 0x30, 0x3b, 0x39, 0x97, 0xea, 0x81, 0xeb, 0x89, 0x2b, + 0x48, 0x3e, 0xdd, 0x0c, 0xa0, 0x4d, 0x37, 0x03, 0xb0, 0x41, 0x34, 0xe9, 0x8e, 0xb0, 0x9d, 0xe3, + 0x20, 0x7a, 0x74, 0x47, 0x0b, 0x8f, 0xa3, 0x3b, 0x4c, 0x15, 0x30, 0xe9, 0x0e, 0x1e, 0xf7, 0xb5, + 0xac, 0x32, 0x3b, 0x09, 0x43, 0x91, 0x40, 0x33, 0x7e, 0x77, 0xb4, 0xe7, 0xba, 0x65, 0xb2, 0xf7, + 0x78, 0xeb, 0x76, 0xd5, 0x1e, 0x60, 0xdd, 0xbe, 0x16, 0xfa, 0x81, 0xaa, 0x31, 0xa1, 0x08, 0x51, + 0x85, 0x3f, 0xc7, 0x99, 0xff, 0xa5, 0xc2, 0x71, 0x16, 0x91, 0x18, 0xa4, 0xec, 0xa0, 0x83, 0x94, + 0x1b, 0x68, 0x90, 0x48, 0x19, 0x26, 0xc2, 0xbc, 0x44, 0x1b, 0x76, 0xa0, 0xc9, 0xa6, 0x30, 0x99, + 0x94, 0xd5, 0xb1, 0x03, 0x55, 0x36, 0xe9, 0x24, 0xe4, 0x6d, 0x18, 0x13, 0xce, 0xd0, 0xc8, 0x61, + 0x28, 0x72, 0x47, 0x93, 0x9e, 0xd3, 0x31, 0x7a, 0x15, 0x9d, 0x7d, 0x92, 0x1b, 0x4e, 0x87, 0x36, + 0x9d, 0x36, 0xad, 0xa1, 0xed, 0x5c, 0xac, 0x18, 0xfc, 0x24, 0x3b, 0xa2, 0xc4, 0xe2, 0x66, 0x75, + 0xcd, 0x6a, 0xa6, 0x11, 0xc5, 0x17, 0xeb, 0xc8, 0xb1, 0x16, 0x2b, 0xf7, 0x06, 0xf1, 0x56, 0xdd, + 0x5d, 0x47, 0xfa, 0xbf, 0x49, 0x6f, 0x10, 0xcf, 0x6a, 0x32, 0x68, 0xcc, 0x1b, 0x84, 0xa3, 0x32, + 0xbd, 0x9e, 0xfd, 0xa8, 0x56, 0xc4, 0x3d, 0x0d, 0xea, 0xf5, 0x48, 0xa4, 0x3b, 0x1d, 0x72, 0x24, + 0x59, 0xcd, 0x72, 0xcb, 0x76, 0x9a, 0x22, 0xf4, 0x2f, 0xaa, 0x86, 0x32, 0x68, 0xbc, 0x1a, 0x44, + 0x25, 0x75, 0x18, 0x37, 0xe9, 0xce, 0x86, 0xe7, 0x06, 0xb4, 0x1e, 0xd0, 0x86, 0xd0, 0x65, 0xa4, + 0x3a, 0x5f, 0x76, 0x5d, 0xae, 0xa7, 0x95, 0x5f, 0xff, 0xdd, 0xc3, 0x85, 0xcc, 0x77, 0x0f, 0x17, + 0x80, 0x81, 0xb8, 0x47, 0xeb, 0xe3, 0xc3, 0x85, 0x53, 0x6c, 0xfe, 0x3b, 0x92, 0x58, 0xdd, 0x62, + 0x54, 0xa6, 0xe4, 0x7b, 0x99, 0xd0, 0x0d, 0x87, 0x24, 0xaa, 0x6c, 0xbc, 0x47, 0x65, 0x6f, 0xa4, + 0x56, 0xb6, 0xa0, 0x8c, 0x76, 0x6a, 0xa5, 0xa9, 0x95, 0x90, 0x77, 0x60, 0x6c, 0xa9, 0xba, 0xe4, + 0xb6, 0x77, 0x9c, 0xdd, 0xda, 0x4a, 0x09, 0x15, 0x22, 0xe1, 0xcd, 0x5c, 0x77, 0xac, 0x3a, 0xc2, + 0x2d, 0x7f, 0xcf, 0xd6, 0x82, 0x5a, 0x22, 0x7c, 0x72, 0x0b, 0x26, 0xe5, 0x4f, 0x93, 0xee, 0xdc, + 0x33, 0xab, 0xa8, 0x07, 0x49, 0x17, 0xf2, 0x90, 0x03, 0x1b, 0x88, 0xae, 0xa7, 0xea, 0xc7, 0x31, + 0x32, 0xb6, 0x18, 0x2b, 0xb4, 0xd3, 0x74, 0x0f, 0x58, 0xf3, 0x36, 0x1d, 0xea, 0xa1, 0xe6, 0x23, + 0x16, 0x63, 0x23, 0x2c, 0xb1, 0x02, 0x47, 0x13, 0xb7, 0x31, 0x22, 0xb2, 0x06, 0xd3, 0x62, 0x89, + 0xdf, 0x77, 0x7c, 0x67, 0xdb, 0x69, 0x3a, 0xc1, 0x01, 0x86, 0x19, 0x0a, 0x2d, 0x44, 0x7e, 0x17, + 0x0f, 0xc3, 0x52, 0x85, 0x59, 0x92, 0xd4, 0xf8, 0x95, 0x2c, 0x9c, 0xed, 0xa7, 0xff, 0x93, 0x9a, + 0x2e, 0xcc, 0x2e, 0x0d, 0x70, 0x66, 0x38, 0x5a, 0x9c, 0x2d, 0xc3, 0xe4, 0xba, 0xb7, 0x6b, 0xb7, + 0x9d, 0x6f, 0xe3, 0xb9, 0x2e, 0x74, 0x8a, 0xc1, 0xc1, 0x70, 0x95, 0x12, 0x7d, 0xb5, 0xc7, 0x88, + 0xe6, 0x1f, 0x0a, 0x31, 0xf7, 0x69, 0xc3, 0x2b, 0x6e, 0xc0, 0xe8, 0x92, 0xdb, 0x0e, 0xe8, 0xa3, + 0x20, 0x16, 0x05, 0xc8, 0x81, 0xf1, 0xd0, 0x12, 0x89, 0x6a, 0xfc, 0xbf, 0x59, 0x38, 0xd7, 0x57, + 0x01, 0x26, 0x9b, 0xfa, 0xa8, 0x5d, 0x1e, 0x44, 0x6b, 0x3e, 0x7a, 0xd8, 0x16, 0x13, 0xfe, 0x1b, + 0x47, 0x7a, 0x2f, 0xcf, 0xff, 0xd7, 0x19, 0x31, 0x48, 0x5f, 0x80, 0x11, 0xac, 0x2a, 0x1c, 0x22, + 0x6e, 0x1b, 0x42, 0x29, 0xec, 0xe8, 0xb6, 0x21, 0x8e, 0x46, 0xae, 0x43, 0x61, 0xc9, 0x6e, 0x36, + 0x95, 0x18, 0x49, 0xd4, 0xeb, 0xeb, 0x08, 0x8b, 0xb9, 0xfb, 0x48, 0x44, 0xf2, 0x26, 0x00, 0xff, + 0x5b, 0xd9, 0x2b, 0x50, 0x58, 0x0a, 0xb2, 0xd8, 0x76, 0xa1, 0x20, 0x63, 0x66, 0xb5, 0xba, 0x1b, + 0x06, 0x73, 0xf1, 0xcc, 0x6a, 0x0c, 0xa0, 0x65, 0x56, 0x63, 0x00, 0xe3, 0x57, 0x73, 0xf0, 0x7c, + 0xff, 0x53, 0x1c, 0xb9, 0xa7, 0x4f, 0xc1, 0x2b, 0x03, 0x9d, 0xfd, 0x8e, 0x9e, 0x03, 0x99, 0xa7, + 0x90, 0x0f, 0xc8, 0xa5, 0xa4, 0x93, 0xf1, 0x27, 0x87, 0x0b, 0x8a, 0x0f, 0xd9, 0x6d, 0xd7, 0x69, + 0x2b, 0x37, 0x05, 0xdf, 0x02, 0xa8, 0x05, 0x76, 0xe0, 0xd4, 0x6f, 0x6f, 0xdd, 0x91, 0x61, 0xfc, + 0x37, 0x06, 0x6b, 0x59, 0x44, 0xc7, 0xe5, 0x8a, 0x88, 0x6f, 0x40, 0xa8, 0xf5, 0xf1, 0xfe, 0x03, + 0xed, 0x9c, 0x1a, 0x21, 0xcf, 0x7f, 0x05, 0x8a, 0x71, 0x52, 0x72, 0x11, 0xf2, 0xd8, 0x00, 0xc5, + 0x53, 0x3a, 0xc6, 0x01, 0xcb, 0xe7, 0xef, 0x8a, 0xb5, 0xb3, 0x0c, 0x93, 0xe2, 0x7a, 0x5a, 0xb7, + 0x88, 0xe1, 0xf7, 0x2a, 0x6f, 0xb7, 0x93, 0x56, 0xb1, 0x18, 0x91, 0xf1, 0x67, 0x19, 0x38, 0xdd, + 0xf3, 0x7c, 0x4c, 0x36, 0xf4, 0x09, 0x7b, 0xe9, 0xa8, 0x03, 0xf5, 0x91, 0x73, 0x35, 0xff, 0x63, + 0x72, 0xed, 0xbf, 0x0b, 0xe3, 0xb5, 0xee, 0x76, 0xfc, 0x90, 0xc5, 0x83, 0xba, 0x15, 0xb8, 0xba, + 0x83, 0xa9, 0xf8, 0xac, 0xff, 0xf2, 0xfe, 0x5d, 0xb8, 0x57, 0xf0, 0x83, 0x1f, 0xf6, 0x3f, 0x0c, + 0x8f, 0xc2, 0x00, 0x3c, 0x75, 0x10, 0x63, 0x44, 0xc6, 0x2f, 0x67, 0xd3, 0x4f, 0xab, 0xec, 0xac, + 0x7d, 0x8c, 0xd3, 0xea, 0xad, 0xa5, 0x8d, 0xa3, 0xfb, 0xfe, 0x9f, 0xca, 0xbe, 0xe3, 0x75, 0xa4, + 0x90, 0x78, 0xd2, 0xbc, 0x27, 0xae, 0x23, 0xa5, 0x74, 0xf4, 0xf5, 0xeb, 0x48, 0x89, 0x4c, 0xde, + 0x80, 0xd1, 0x55, 0x97, 0x07, 0xc6, 0xca, 0x1e, 0xf3, 0xf8, 0x21, 0x09, 0x54, 0xc5, 0x63, 0x88, + 0xc9, 0xce, 0x16, 0xfa, 0xc4, 0x4b, 0x27, 0x6f, 0x3c, 0x5b, 0xc4, 0x96, 0x8b, 0x6e, 0x04, 0xd3, + 0xc9, 0x8c, 0x1f, 0xcb, 0xc2, 0x24, 0x5f, 0xbc, 0xdc, 0x48, 0xfb, 0xcc, 0x1a, 0xc0, 0xdf, 0xd2, + 0x0c, 0xe0, 0x32, 0xff, 0x83, 0xda, 0xb5, 0x81, 0xcc, 0xdf, 0x7b, 0x40, 0x92, 0x34, 0xc4, 0x84, + 0x71, 0x15, 0xda, 0xdf, 0xf2, 0x7d, 0x2d, 0x4a, 0x15, 0x22, 0x64, 0x07, 0x5e, 0x3f, 0xf8, 0xa6, + 0xc6, 0xc3, 0xf8, 0xab, 0x59, 0x98, 0x50, 0xae, 0x2b, 0x9f, 0xd9, 0x81, 0xff, 0x8a, 0x36, 0xf0, + 0x73, 0xa1, 0x63, 0x72, 0xd8, 0xb3, 0x81, 0xc6, 0xbd, 0x0b, 0xd3, 0x09, 0x92, 0xf8, 0xad, 0x6f, + 0x66, 0x90, 0x5b, 0xdf, 0xd7, 0x92, 0x79, 0x07, 0x78, 0xfa, 0xce, 0x30, 0x98, 0x55, 0x4d, 0x74, + 0xf0, 0x13, 0x59, 0x98, 0x15, 0xbf, 0x30, 0x51, 0x0f, 0x97, 0xde, 0xcf, 0xec, 0x5c, 0x94, 0xb4, + 0xb9, 0x58, 0xd0, 0xe7, 0x42, 0xe9, 0x60, 0xef, 0x29, 0x31, 0xfe, 0x32, 0xc0, 0x5c, 0x2f, 0x82, + 0x81, 0xe3, 0x7f, 0x22, 0xef, 0xea, 0xec, 0x00, 0xde, 0xd5, 0xab, 0x50, 0xc4, 0xaa, 0x44, 0x2a, + 0x0e, 0x9f, 0x9d, 0x01, 0x72, 0x91, 0xc2, 0xcd, 0xb3, 0x29, 0x89, 0xd4, 0x20, 0x7e, 0xec, 0x10, + 0x90, 0xa0, 0x24, 0xbf, 0x98, 0x81, 0x49, 0x04, 0x2e, 0x3f, 0xa4, 0xed, 0x00, 0x99, 0xe5, 0x85, + 0x33, 0x70, 0x68, 0x1f, 0xaf, 0x05, 0x9e, 0xd3, 0xde, 0x15, 0x06, 0xf2, 0x6d, 0x61, 0x20, 0x7f, + 0x9b, 0x1b, 0xf6, 0xaf, 0xd4, 0xdd, 0xd6, 0xd5, 0x5d, 0xcf, 0x7e, 0xe8, 0xf0, 0x9b, 0x78, 0xbb, + 0x79, 0x35, 0xca, 0x1e, 0xdd, 0x71, 0x62, 0xf9, 0xa0, 0x05, 0x2b, 0xbc, 0x7c, 0xe0, 0x0d, 0xa5, + 0x58, 0x6d, 0xfc, 0xac, 0xa2, 0xb7, 0x88, 0x7c, 0x0f, 0x9c, 0xe2, 0x71, 0xf6, 0x4c, 0xe5, 0x75, + 0xda, 0x5d, 0xb7, 0xeb, 0x97, 0xed, 0xfa, 0x03, 0xb6, 0xef, 0xf1, 0x80, 0x06, 0xec, 0x79, 0x3d, + 0x2c, 0xb4, 0xb6, 0x79, 0xa9, 0x16, 0xc0, 0x95, 0xce, 0x80, 0xac, 0xc0, 0x34, 0x2f, 0x2a, 0x75, + 0x03, 0xb7, 0x56, 0xb7, 0x9b, 0x4e, 0x7b, 0x17, 0xcf, 0xd4, 0x05, 0xbe, 0x1f, 0xdb, 0xdd, 0xc0, + 0xb5, 0x7c, 0x0e, 0x57, 0x8f, 0x2e, 0x09, 0x22, 0x52, 0x85, 0x29, 0x93, 0xda, 0x8d, 0xbb, 0xf6, + 0xa3, 0x25, 0xbb, 0x63, 0xd7, 0xd9, 0x41, 0xa8, 0x80, 0x97, 0x49, 0x78, 0x36, 0xf3, 0xa8, 0xdd, + 0xb0, 0x5a, 0xf6, 0x23, 0xab, 0x2e, 0x0a, 0x75, 0x1b, 0x96, 0x46, 0x17, 0xb2, 0x72, 0xda, 0x21, + 0xab, 0xd1, 0x38, 0x2b, 0xa7, 0xdd, 0x9b, 0x55, 0x44, 0x27, 0x59, 0x6d, 0xda, 0xde, 0x2e, 0x0d, + 0xb8, 0x23, 0x1b, 0x3b, 0x8f, 0x67, 0x14, 0x56, 0x01, 0x96, 0x59, 0xe8, 0xd4, 0x16, 0x67, 0xa5, + 0xd0, 0xb1, 0x95, 0xb7, 0xe5, 0x39, 0x01, 0x55, 0x7b, 0x38, 0x86, 0xcd, 0xc2, 0xf1, 0x47, 0x17, + 0xc0, 0x5e, 0x5d, 0x4c, 0x50, 0x46, 0xdc, 0x94, 0x4e, 0x8e, 0x27, 0xb8, 0xa5, 0xf7, 0x32, 0x41, + 0x19, 0x72, 0x53, 0xfb, 0x39, 0x81, 0xfd, 0x54, 0xb8, 0xf5, 0xe8, 0x68, 0x82, 0x92, 0xac, 0xb1, + 0x41, 0x0b, 0x68, 0x9b, 0xad, 0x68, 0xe1, 0xc8, 0x37, 0x89, 0x4d, 0x7b, 0x51, 0x78, 0xa3, 0x14, + 0x3d, 0x59, 0x6c, 0xa5, 0xb8, 0xf5, 0xc5, 0x89, 0xc9, 0xf7, 0xc1, 0xd4, 0x3d, 0x9f, 0xde, 0xac, + 0x6e, 0xd4, 0x64, 0x58, 0x3e, 0x9e, 0xb6, 0x27, 0x17, 0xaf, 0x1d, 0x21, 0x74, 0xae, 0xa8, 0x34, + 0x98, 0xc4, 0x99, 0xcf, 0x5b, 0xd7, 0xa7, 0xd6, 0x8e, 0xd3, 0xf1, 0xc3, 0x1c, 0x27, 0xea, 0xbc, + 0xc5, 0xaa, 0x32, 0x56, 0x60, 0x3a, 0xc1, 0x86, 0x4c, 0x02, 0x30, 0xa0, 0x75, 0x6f, 0xad, 0xb6, + 0xbc, 0x59, 0x7c, 0x8e, 0x14, 0x61, 0x1c, 0x7f, 0x2f, 0xaf, 0x95, 0xca, 0xab, 0xcb, 0x95, 0x62, + 0x86, 0x4c, 0xc3, 0x04, 0x42, 0x2a, 0xd5, 0x1a, 0x07, 0x65, 0x79, 0x0a, 0x4f, 0xb3, 0xc8, 0x3f, + 0xdd, 0x80, 0x7d, 0x00, 0xb8, 0xa7, 0x18, 0x7f, 0x23, 0x0b, 0xa7, 0xe5, 0xb6, 0x42, 0x83, 0x7d, + 0xd7, 0x7b, 0xe0, 0xb4, 0x77, 0x9f, 0xf1, 0xdd, 0xe1, 0xa6, 0xb6, 0x3b, 0xbc, 0x18, 0xdb, 0xa9, + 0x63, 0xbd, 0xec, 0xb3, 0x45, 0xfc, 0xd3, 0x02, 0x9c, 0xeb, 0x4b, 0x45, 0xbe, 0xca, 0x76, 0x73, + 0x87, 0xb6, 0x83, 0x6a, 0xa3, 0x49, 0x37, 0x9d, 0x16, 0x75, 0xbb, 0x81, 0x70, 0x1c, 0xbd, 0x80, + 0x07, 0x5c, 0x2c, 0xb4, 0x9c, 0x46, 0x93, 0x5a, 0x01, 0x2f, 0xd6, 0x96, 0x5b, 0x92, 0x9a, 0xb1, + 0x0c, 0x13, 0xca, 0x57, 0xdb, 0x01, 0xf5, 0x1e, 0xa2, 0x73, 0x4a, 0xc8, 0xf2, 0x01, 0xa5, 0x1d, + 0xcb, 0x66, 0xa5, 0x96, 0x23, 0x8a, 0x75, 0x96, 0x09, 0x6a, 0x72, 0x53, 0x61, 0xb9, 0xc4, 0xd4, + 0xe1, 0xbb, 0xf6, 0x23, 0x71, 0x5b, 0x2e, 0xf2, 0x33, 0x85, 0x2c, 0x79, 0xcc, 0x51, 0xcb, 0x7e, + 0x64, 0x26, 0x49, 0xc8, 0x47, 0x70, 0x42, 0x6c, 0x40, 0x22, 0x66, 0x54, 0xf6, 0x98, 0x47, 0xa4, + 0xbe, 0xfc, 0xf8, 0x70, 0xe1, 0x94, 0xcc, 0x6c, 0x25, 0xa3, 0x84, 0xd3, 0x7a, 0x9d, 0xce, 0x85, + 0x6c, 0xb2, 0x0d, 0x39, 0x36, 0x1c, 0x77, 0xa9, 0xef, 0xdb, 0xbb, 0xf2, 0x66, 0x9d, 0x7b, 0xd9, + 0x2b, 0x83, 0x69, 0xb5, 0x78, 0xb9, 0xd9, 0x93, 0x92, 0xac, 0xc0, 0xe4, 0x16, 0xdd, 0x56, 0xe7, + 0x67, 0x38, 0x14, 0x55, 0xc5, 0x7d, 0xba, 0xdd, 0x7b, 0x72, 0x62, 0x74, 0xc4, 0x41, 0x83, 0xd9, + 0xa3, 0x83, 0x55, 0xc7, 0x0f, 0x68, 0x9b, 0x7a, 0x98, 0x8b, 0x60, 0x04, 0x85, 0xc1, 0x5c, 0xa4, + 0x21, 0xeb, 0xe5, 0xe5, 0x17, 0x1e, 0x1f, 0x2e, 0x9c, 0xe3, 0x51, 0x25, 0x4d, 0x01, 0xb7, 0x62, + 0xe9, 0xd8, 0x93, 0x5c, 0xc9, 0x37, 0x61, 0xca, 0x74, 0xbb, 0x81, 0xd3, 0xde, 0xad, 0x05, 0x9e, + 0x1d, 0xd0, 0x5d, 0xbe, 0x21, 0x45, 0x49, 0x0f, 0x62, 0xa5, 0xe2, 0xae, 0x85, 0x03, 0x2d, 0x5f, + 0x40, 0xb5, 0x1d, 0x41, 0x27, 0x20, 0xdf, 0x80, 0x49, 0x1e, 0x2d, 0x18, 0x56, 0x30, 0xaa, 0xa5, + 0x92, 0xd5, 0x0b, 0xef, 0x5f, 0xe3, 0x07, 0x54, 0x1e, 0x75, 0x98, 0x56, 0x41, 0x8c, 0x1b, 0xf9, + 0x50, 0x0c, 0xd6, 0x86, 0xd3, 0xde, 0x0d, 0x97, 0x31, 0xe0, 0xc8, 0xbf, 0x1e, 0x0d, 0x49, 0x87, + 0x35, 0x57, 0x2e, 0xe3, 0x1e, 0x9e, 0x1a, 0x49, 0x3e, 0x24, 0x80, 0x73, 0x25, 0xdf, 0x77, 0xfc, + 0x40, 0xb8, 0x57, 0x2f, 0x3f, 0xa2, 0xf5, 0x2e, 0x43, 0xde, 0x72, 0xbd, 0x07, 0xd4, 0xe3, 0xee, + 0x7d, 0x43, 0xe5, 0x2b, 0x8f, 0x0f, 0x17, 0x5e, 0xb1, 0x11, 0xd1, 0x12, 0x1e, 0xd9, 0x16, 0x95, + 0xa8, 0xd6, 0x3e, 0xc7, 0x55, 0xfa, 0xd0, 0x9f, 0x29, 0xf9, 0x06, 0x9c, 0x5c, 0xb2, 0x7d, 0x5a, + 0x6d, 0xfb, 0xb4, 0xed, 0x3b, 0x81, 0xf3, 0x90, 0x8a, 0x41, 0xc5, 0xcd, 0xaf, 0x80, 0x89, 0xeb, + 0x8d, 0xba, 0xed, 0xb3, 0x0f, 0x33, 0x44, 0xb1, 0xc4, 0xa4, 0x28, 0xd5, 0xf4, 0xe0, 0x62, 0x1c, + 0x66, 0xa0, 0x18, 0x1f, 0x76, 0xf2, 0x35, 0x18, 0xe5, 0x2e, 0x09, 0xd4, 0xdf, 0x13, 0x81, 0x6e, + 0xf2, 0x86, 0x3b, 0x84, 0xeb, 0x44, 0x22, 0x34, 0x81, 0x3b, 0x3c, 0x50, 0xf5, 0xbe, 0x16, 0x43, + 0x13, 0x24, 0x11, 0x69, 0xc0, 0x38, 0x1f, 0x59, 0x8a, 0xd9, 0x49, 0x84, 0x67, 0xda, 0x0b, 0xea, + 0x4a, 0x16, 0x45, 0x31, 0xfe, 0x68, 0xf2, 0x16, 0xf3, 0xc7, 0x11, 0xb4, 0x2a, 0x34, 0xae, 0x65, + 0x80, 0x82, 0x24, 0x34, 0x4e, 0xc3, 0xa9, 0x1e, 0x6d, 0x36, 0x1e, 0xe2, 0x35, 0x58, 0x8f, 0x1a, + 0xc9, 0xd7, 0x60, 0x16, 0x09, 0x97, 0xdc, 0x76, 0x9b, 0xd6, 0x03, 0x14, 0x1d, 0xd2, 0x74, 0x94, + 0xe3, 0x77, 0xad, 0xbc, 0xbf, 0xf5, 0x10, 0xc1, 0x8a, 0x5b, 0x90, 0x52, 0x39, 0x18, 0x3f, 0x9b, + 0x85, 0x39, 0x21, 0x8d, 0x4c, 0x5a, 0x77, 0xbd, 0xc6, 0xb3, 0xbf, 0xfb, 0x2d, 0x6b, 0xbb, 0xdf, + 0x85, 0x30, 0xb2, 0x39, 0xad, 0x93, 0x7d, 0x36, 0xbf, 0x5f, 0xce, 0xc0, 0xd9, 0x7e, 0x44, 0x6c, + 0x74, 0xc2, 0x6c, 0x2c, 0xa3, 0x89, 0xac, 0x2b, 0x1d, 0x98, 0xc1, 0x09, 0x5d, 0xda, 0xa3, 0xf5, + 0x07, 0xfe, 0x8a, 0xeb, 0x07, 0xe8, 0x1c, 0x9b, 0xed, 0x71, 0x51, 0xf3, 0x5a, 0xea, 0x45, 0xcd, + 0x49, 0xbe, 0xca, 0xea, 0xc8, 0x83, 0xe7, 0x8b, 0x79, 0x40, 0x0f, 0x7c, 0x33, 0x8d, 0x35, 0x3a, + 0x39, 0x96, 0xba, 0xc1, 0xde, 0x86, 0x47, 0x77, 0xa8, 0x47, 0xdb, 0x75, 0xfa, 0x39, 0x73, 0x72, + 0xd4, 0x3b, 0x37, 0x90, 0xb5, 0xe1, 0x5f, 0x9b, 0x80, 0xd9, 0x34, 0x32, 0x36, 0x2e, 0xca, 0x01, + 0x37, 0xfe, 0x06, 0xce, 0x0f, 0x67, 0x60, 0xbc, 0x46, 0xeb, 0x6e, 0xbb, 0x71, 0x13, 0xaf, 0xc3, + 0xc5, 0xe8, 0x58, 0x7c, 0x83, 0x67, 0x70, 0x6b, 0x27, 0x76, 0x4f, 0xfe, 0xc9, 0xe1, 0xc2, 0xfb, + 0x83, 0x9d, 0x2b, 0xeb, 0x2e, 0x46, 0x27, 0x07, 0x98, 0xa3, 0x35, 0xac, 0x02, 0x2d, 0xdb, 0x5a, + 0xa5, 0xa4, 0x0c, 0x13, 0xe2, 0x73, 0x75, 0xd5, 0x64, 0x3c, 0x3c, 0xf8, 0x5b, 0x16, 0x24, 0x12, + 0xa8, 0x69, 0x24, 0xe4, 0x3a, 0xe4, 0xee, 0x2d, 0xde, 0x14, 0x73, 0x20, 0xb3, 0xdc, 0xde, 0x5b, + 0xbc, 0x89, 0xa6, 0x2b, 0x76, 0x1c, 0x98, 0xe8, 0x2e, 0x6a, 0x37, 0xd4, 0xf7, 0x16, 0x6f, 0x92, + 0xef, 0x87, 0x13, 0x15, 0xc7, 0x17, 0x55, 0x70, 0x77, 0xdb, 0x06, 0x06, 0x99, 0x0c, 0xf7, 0x58, + 0xbd, 0x5f, 0x4a, 0x5d, 0xbd, 0x2f, 0x34, 0x42, 0x26, 0x16, 0xf7, 0xe5, 0x6d, 0xc4, 0x93, 0x0e, + 0xa5, 0xd7, 0x43, 0x3e, 0x86, 0x49, 0x34, 0xbd, 0xa2, 0x07, 0x32, 0xe6, 0x79, 0x1c, 0xe9, 0x51, + 0xf3, 0x17, 0x52, 0x6b, 0x9e, 0x47, 0x4b, 0xae, 0x85, 0x7e, 0xcc, 0x98, 0x13, 0x52, 0x3b, 0xa1, + 0x6b, 0x9c, 0xc9, 0x6d, 0x98, 0x12, 0xaa, 0xd2, 0xfa, 0xce, 0xe6, 0x1e, 0xad, 0xd8, 0x07, 0xe2, + 0x72, 0x19, 0x4f, 0x5f, 0x42, 0xbf, 0xb2, 0xdc, 0x1d, 0x2b, 0xd8, 0xa3, 0x56, 0xc3, 0xd6, 0x94, + 0x8a, 0x18, 0x21, 0xf9, 0x5e, 0x18, 0x5b, 0x75, 0xeb, 0x4c, 0x4b, 0x46, 0xc9, 0xc0, 0xef, 0x9b, + 0x3f, 0xc0, 0x57, 0x56, 0x38, 0x38, 0xa6, 0xfa, 0x7c, 0x72, 0xb8, 0xf0, 0xd6, 0x71, 0x17, 0x8d, + 0x52, 0x81, 0xa9, 0xd6, 0x46, 0x96, 0xa0, 0xb0, 0x45, 0xb7, 0x59, 0x6f, 0xe3, 0x2f, 0x30, 0x48, + 0xb0, 0x70, 0x27, 0x11, 0xbf, 0x34, 0x77, 0x12, 0x01, 0x23, 0x1e, 0x4c, 0xe3, 0xf8, 0x6c, 0xd8, + 0xbe, 0xbf, 0xef, 0x7a, 0x0d, 0x4c, 0xb5, 0xdb, 0xeb, 0x2a, 0x7b, 0x31, 0x75, 0xf0, 0xcf, 0xf2, + 0xc1, 0xef, 0x28, 0x1c, 0x54, 0x65, 0x2f, 0xc1, 0x9e, 0x7c, 0x13, 0x26, 0x4d, 0xfa, 0xad, 0xae, + 0xe3, 0xd1, 0xbb, 0x37, 0x4b, 0xf8, 0x55, 0x8e, 0x6b, 0xa1, 0x3a, 0x7a, 0x21, 0xd7, 0x28, 0x3d, + 0x0e, 0x93, 0xd6, 0x22, 0xab, 0xb5, 0x63, 0xeb, 0xb7, 0x05, 0x2a, 0x09, 0xd9, 0x80, 0xb1, 0x0a, + 0x7d, 0xe8, 0xd4, 0x29, 0x86, 0x13, 0x08, 0x57, 0xbe, 0x30, 0x85, 0x7c, 0x54, 0xc2, 0xed, 0x26, + 0x0d, 0x04, 0xf0, 0xe0, 0x04, 0xdd, 0x5b, 0x2c, 0x44, 0x24, 0x37, 0x20, 0x57, 0xad, 0x6c, 0x08, + 0x4f, 0x3e, 0xe9, 0xa1, 0x5f, 0x6d, 0x6c, 0xc8, 0x84, 0xdb, 0xe8, 0xfc, 0xe1, 0x34, 0x34, 0x3f, + 0xc0, 0x6a, 0x65, 0x83, 0xec, 0xc0, 0x04, 0x0e, 0xc0, 0x0a, 0xb5, 0xf9, 0xd8, 0x4e, 0xf5, 0x18, + 0xdb, 0x2b, 0xa9, 0x63, 0x3b, 0xc7, 0xc7, 0x76, 0x4f, 0x50, 0x6b, 0x19, 0x84, 0x55, 0xb6, 0x4c, + 0xfd, 0x14, 0x59, 0xcd, 0x65, 0xde, 0xdb, 0xcd, 0x55, 0xbc, 0xdc, 0x16, 0xea, 0xa7, 0x4c, 0x82, + 0x1e, 0x26, 0xe2, 0xed, 0xe9, 0x28, 0x9c, 0xe4, 0x43, 0xbe, 0x02, 0xf9, 0xf5, 0x07, 0x81, 0x3d, + 0x37, 0xad, 0x8d, 0x23, 0x03, 0xc9, 0xee, 0xa3, 0xc5, 0xd0, 0x7d, 0xa0, 0xa5, 0xa5, 0x40, 0x1a, 0xb2, 0x08, 0x23, 0x1b, 0xd5, 0xfb, 0xb5, 0xa6, 0x1b, 0xcc, 0x91, 0xf0, 0x4c, 0x43, 0x3a, 0xce, - 0x03, 0xcb, 0x6f, 0xba, 0xfa, 0xcb, 0x08, 0x12, 0x91, 0x4d, 0xdf, 0x8a, 0xed, 0x35, 0x0e, 0x6c, + 0x43, 0xcb, 0x6f, 0xba, 0xfa, 0xcb, 0x08, 0x12, 0x91, 0x4d, 0xdf, 0x8a, 0xed, 0x35, 0xf6, 0x6d, 0x0f, 0xe3, 0xc0, 0x66, 0xb4, 0x6a, 0x95, 0x12, 0x3e, 0x7d, 0x7b, 0x02, 0x10, 0x0b, 0x0e, 0x53, - 0x59, 0x90, 0xef, 0x81, 0xb3, 0xbe, 0xb3, 0xdb, 0xb6, 0x83, 0xae, 0x47, 0x2d, 0xbb, 0xb9, 0xeb, + 0x59, 0x90, 0xef, 0x81, 0xd3, 0xbe, 0xb3, 0xdb, 0xb6, 0x83, 0xae, 0x47, 0x2d, 0xbb, 0xb9, 0xeb, 0x7a, 0x4e, 0xb0, 0xd7, 0xb2, 0xfc, 0xae, 0x13, 0xd0, 0xb9, 0x59, 0xed, 0xdd, 0xb4, 0x9a, 0xc4, - 0x2b, 0x49, 0xb4, 0x1a, 0xc3, 0x32, 0xcf, 0xf8, 0xe9, 0x05, 0xc2, 0xd2, 0x30, 0x2d, 0x96, 0xa0, - 0x18, 0xb6, 0xbb, 0x37, 0x4b, 0xc6, 0x7f, 0x98, 0x41, 0x61, 0x4c, 0x5e, 0xc1, 0xa8, 0xf8, 0xf0, + 0x2b, 0x49, 0xb4, 0x1a, 0xc3, 0x32, 0x4f, 0xf9, 0xe9, 0x05, 0xc2, 0xd2, 0x30, 0x2d, 0x96, 0xa0, + 0x18, 0xb6, 0xbb, 0x37, 0x4b, 0xc6, 0x7f, 0x94, 0x41, 0x61, 0x4c, 0x5e, 0xc1, 0xa8, 0xf8, 0xf0, 0xf2, 0x18, 0x6d, 0xa6, 0x76, 0x27, 0x96, 0x87, 0x92, 0xa3, 0x90, 0xd7, 0x60, 0xf8, 0xa6, 0x5d, 0xa7, 0x81, 0xbc, 0x34, 0x42, 0xe4, 0x1d, 0x84, 0xa8, 0x06, 0x56, 0x8e, 0xc3, 0xf4, 0x44, 0xbe, - 0x48, 0x4b, 0xd1, 0x73, 0x77, 0xe5, 0x92, 0xbc, 0x33, 0x42, 0x3d, 0x51, 0x2c, 0x6e, 0xe5, 0x3d, - 0xbc, 0x98, 0x7f, 0x65, 0x2a, 0x07, 0xe3, 0x8f, 0x33, 0x91, 0x74, 0x21, 0x2f, 0x43, 0xde, 0xdc, - 0x08, 0xdb, 0xcf, 0xe3, 0xad, 0x62, 0xcd, 0x47, 0x04, 0xf2, 0x21, 0x9c, 0x52, 0xf8, 0x24, 0x9c, - 0x3d, 0x5f, 0xc2, 0x70, 0x20, 0xa5, 0x25, 0xe9, 0x1e, 0x9f, 0xe9, 0x3c, 0x50, 0x29, 0x8e, 0x0a, - 0x2a, 0xb4, 0xed, 0x70, 0xde, 0x4a, 0x67, 0x55, 0xde, 0x0d, 0x44, 0x88, 0x77, 0x36, 0x8d, 0x03, - 0x8f, 0x06, 0x32, 0x7e, 0x3d, 0xa3, 0x49, 0x8d, 0xf0, 0x5d, 0xb1, 0xcc, 0x31, 0xef, 0x8a, 0xbd, - 0x09, 0x50, 0xea, 0x06, 0xee, 0x72, 0xdb, 0x73, 0x9b, 0xdc, 0x72, 0x21, 0x52, 0xb1, 0xa2, 0x3d, - 0x96, 0x22, 0x58, 0x0b, 0x5a, 0x08, 0x91, 0x53, 0xfd, 0x62, 0x73, 0x9f, 0xd4, 0x2f, 0xd6, 0xf8, - 0xdd, 0x8c, 0xf6, 0xdd, 0x30, 0x6d, 0x4f, 0x7e, 0x7a, 0x8a, 0xdb, 0x42, 0xf2, 0xd3, 0x8b, 0x3e, - 0xbc, 0x7f, 0x35, 0x03, 0xa7, 0xb9, 0x83, 0xe9, 0x5a, 0xb7, 0xb5, 0x4d, 0xbd, 0xfb, 0x76, 0xd3, - 0x69, 0xf0, 0x98, 0x37, 0xae, 0xc8, 0x5e, 0x4a, 0x7e, 0x84, 0xe9, 0xf8, 0xfc, 0x70, 0xc8, 0x1d, - 0x5e, 0xad, 0x36, 0x16, 0x5a, 0x0f, 0xc2, 0x52, 0xf5, 0x70, 0x98, 0x4e, 0x6f, 0xfc, 0x72, 0x06, - 0x5e, 0x38, 0xb6, 0x16, 0x72, 0x15, 0x46, 0x64, 0x0e, 0xdc, 0x0c, 0x0e, 0x3c, 0x3a, 0x7b, 0x25, - 0xf3, 0xdf, 0x4a, 0x2c, 0xf2, 0x55, 0x38, 0xa5, 0xb2, 0xda, 0xf4, 0x6c, 0x47, 0xcd, 0x34, 0x9b, - 0xd2, 0xea, 0x80, 0xa1, 0xc4, 0xb5, 0xae, 0x74, 0x26, 0xc6, 0xff, 0x95, 0x51, 0x5e, 0x1a, 0x7c, - 0x4a, 0x75, 0xf1, 0x1b, 0x9a, 0x2e, 0x2e, 0xf3, 0x11, 0x85, 0xbd, 0x62, 0x65, 0xa9, 0xe7, 0xa7, - 0x29, 0xc5, 0x69, 0x11, 0x01, 0xdf, 0xc9, 0xc2, 0xd8, 0x3d, 0x9f, 0x7a, 0xfc, 0xf2, 0xf4, 0xb3, - 0x95, 0x77, 0x26, 0xec, 0xd7, 0x40, 0x99, 0x41, 0xfe, 0x30, 0x83, 0x46, 0x75, 0x95, 0x82, 0x8d, - 0x86, 0xf2, 0xba, 0x08, 0x8e, 0x06, 0xbe, 0x2b, 0x82, 0x50, 0x9e, 0x3d, 0x64, 0x55, 0x7f, 0x68, - 0x08, 0x5f, 0x9b, 0x5a, 0x25, 0xef, 0xc3, 0xd0, 0x3d, 0x34, 0x11, 0xea, 0xf1, 0xcd, 0x21, 0x7f, - 0x2c, 0xe4, 0x42, 0xba, 0xcb, 0xfe, 0x54, 0xf7, 0x18, 0x2c, 0x23, 0x35, 0x18, 0x29, 0x7b, 0x14, - 0xdf, 0x0d, 0xcc, 0x0f, 0x1e, 0x9d, 0x57, 0xe7, 0x24, 0xf1, 0xe8, 0x3c, 0xc1, 0xc9, 0xf8, 0xe9, - 0x2c, 0x90, 0xa8, 0x8f, 0x98, 0x24, 0xdf, 0x7f, 0x6a, 0x27, 0xfd, 0x3d, 0x6d, 0xd2, 0xcf, 0x25, - 0x26, 0x9d, 0x77, 0x6f, 0xa0, 0xb9, 0xff, 0xcd, 0x0c, 0x9c, 0x4e, 0x27, 0x24, 0x17, 0x60, 0x78, - 0x7d, 0x73, 0x43, 0x86, 0xc8, 0x8b, 0xae, 0xb8, 0x1d, 0x3c, 0xf3, 0x9b, 0xa2, 0x88, 0xbc, 0x0e, - 0xc3, 0x5f, 0x36, 0xcb, 0x6c, 0x1f, 0x52, 0xb2, 0xb9, 0x7e, 0xd3, 0xb3, 0xea, 0xfa, 0x56, 0x24, - 0x90, 0xd4, 0xb9, 0xcd, 0x3d, 0xb1, 0xb9, 0xfd, 0xf1, 0x2c, 0x4c, 0x95, 0xea, 0x75, 0xea, 0xfb, - 0x4c, 0xc9, 0xa1, 0x7e, 0xf0, 0xd4, 0x4e, 0x6c, 0x7a, 0xf0, 0xbb, 0xd6, 0xb7, 0x81, 0x66, 0xf5, - 0xb7, 0x33, 0x70, 0x4a, 0x52, 0x3d, 0x70, 0xe8, 0xc1, 0xe6, 0x9e, 0x47, 0xfd, 0x3d, 0xb7, 0xd9, - 0x18, 0x38, 0x65, 0x34, 0x53, 0xf4, 0x30, 0x0f, 0xa4, 0x7a, 0x93, 0xbe, 0x83, 0x10, 0x4d, 0xd1, - 0xe3, 0xb9, 0x22, 0xaf, 0xc2, 0x48, 0xa9, 0xd3, 0xf1, 0xdc, 0x07, 0xfc, 0xb3, 0x9f, 0x10, 0xc1, - 0x8a, 0x1c, 0xa4, 0x05, 0x37, 0x72, 0x10, 0x6b, 0x46, 0x85, 0xb6, 0x79, 0xb6, 0x9e, 0x09, 0xde, - 0x8c, 0x06, 0x6d, 0xab, 0xfa, 0x31, 0x96, 0x1b, 0x35, 0x20, 0x1b, 0x9e, 0xdb, 0x72, 0x03, 0xda, - 0xe0, 0xfd, 0xc1, 0x98, 0xd0, 0x63, 0xd3, 0x8c, 0x6c, 0x3a, 0x41, 0x53, 0x4b, 0x33, 0x12, 0x30, - 0x80, 0xc9, 0xe1, 0xc6, 0xff, 0x31, 0x04, 0xe3, 0xea, 0xe8, 0x10, 0x83, 0xe7, 0x81, 0x75, 0x3d, - 0x35, 0x30, 0xd9, 0x46, 0x88, 0x29, 0x4a, 0xa2, 0xa8, 0xfe, 0xec, 0xb1, 0x51, 0xfd, 0x5b, 0x30, - 0xb1, 0xe1, 0xb9, 0x1d, 0xd7, 0xa7, 0x0d, 0xfe, 0x9e, 0x2c, 0x17, 0x85, 0x33, 0xca, 0xf9, 0x91, - 0x4d, 0x24, 0xde, 0x41, 0xa2, 0xf5, 0xa4, 0x23, 0xb0, 0xad, 0xf8, 0x6b, 0xb3, 0x3a, 0x1f, 0xee, - 0xde, 0x60, 0xfb, 0x22, 0x29, 0x57, 0xe8, 0xde, 0xc0, 0x20, 0xba, 0x7b, 0x03, 0x83, 0xa8, 0xdf, - 0xda, 0xd0, 0x93, 0xfa, 0xd6, 0xc8, 0x4f, 0x67, 0x60, 0xac, 0xd4, 0x6e, 0x8b, 0x6c, 0x01, 0xc7, - 0x04, 0x4a, 0x7e, 0x55, 0x78, 0x38, 0xbc, 0xf5, 0x89, 0x3c, 0x1c, 0x50, 0x6f, 0xf1, 0x51, 0x53, - 0x8d, 0x2a, 0x54, 0x4f, 0x50, 0x4a, 0x3b, 0xc8, 0x5b, 0x50, 0x0c, 0x17, 0x79, 0xb5, 0xdd, 0xa0, - 0x0f, 0xa9, 0x3f, 0x37, 0x72, 0x3e, 0x77, 0x69, 0x42, 0xa4, 0xe3, 0x53, 0x35, 0xd3, 0x38, 0x22, - 0xd9, 0x04, 0xb0, 0xc3, 0xd5, 0x15, 0x7b, 0xc9, 0x27, 0xb9, 0xfc, 0x84, 0xf6, 0x8c, 0xbf, 0xf1, - 0x12, 0x49, 0xd5, 0x9e, 0x23, 0x3e, 0xa4, 0x05, 0x53, 0xfc, 0x19, 0x1d, 0x7c, 0x5e, 0x17, 0xb3, - 0xce, 0xc2, 0xb1, 0xf3, 0xf0, 0xb2, 0xb0, 0x83, 0x3d, 0x2b, 0x1e, 0xe7, 0xc1, 0x17, 0x7b, 0xad, - 0x94, 0x14, 0xb4, 0x71, 0xde, 0x3c, 0xf9, 0xa1, 0x79, 0x26, 0xd9, 0x5e, 0xbe, 0xe8, 0x7f, 0x3c, - 0x03, 0xa7, 0xd5, 0x45, 0x5f, 0xeb, 0x6e, 0xb7, 0x1c, 0x3c, 0x0b, 0x92, 0x2b, 0x30, 0x2a, 0xd6, - 0x64, 0x78, 0x88, 0x4a, 0x26, 0xcf, 0x8d, 0x50, 0xc8, 0x32, 0x5b, 0x86, 0x8c, 0x87, 0xd0, 0xba, - 0x67, 0x62, 0x72, 0x8a, 0x15, 0x45, 0x4f, 0xb4, 0x79, 0xf8, 0x5b, 0x5f, 0x9f, 0x0c, 0x62, 0xbc, - 0x0b, 0xd3, 0xfa, 0x4c, 0xd4, 0x68, 0x40, 0x2e, 0xc3, 0x88, 0x9c, 0xbe, 0x4c, 0xfa, 0xf4, 0xc9, - 0x72, 0x63, 0x0b, 0x48, 0x82, 0xde, 0x47, 0x57, 0x24, 0x1a, 0x48, 0x57, 0x39, 0x79, 0x11, 0x98, - 0x40, 0x0c, 0xdf, 0x2c, 0x1f, 0xd3, 0x7c, 0x63, 0x19, 0xa9, 0xf1, 0xc7, 0x93, 0x30, 0x93, 0x22, - 0x73, 0x8f, 0xd1, 0x89, 0x16, 0x74, 0x01, 0x31, 0x1a, 0xc6, 0x59, 0x4b, 0xb1, 0xf0, 0xae, 0x7c, - 0x5e, 0xba, 0x8f, 0x38, 0xe8, 0xf7, 0xe6, 0xf4, 0xa7, 0xa1, 0x17, 0xa9, 0xa9, 0x10, 0x86, 0x9e, - 0x58, 0x2a, 0x84, 0x25, 0x98, 0x10, 0xbd, 0x12, 0xe2, 0x6a, 0x38, 0xb2, 0x14, 0x7b, 0xbc, 0xc0, - 0x4a, 0x88, 0x2d, 0x9d, 0x84, 0xf3, 0xf0, 0xdd, 0xe6, 0x03, 0x2a, 0x78, 0x8c, 0xa8, 0x3c, 0xb0, - 0x20, 0x95, 0x87, 0x42, 0x42, 0xfe, 0x5d, 0x7c, 0x09, 0x04, 0x21, 0xaa, 0xcc, 0x2a, 0xf4, 0x93, - 0x59, 0x8d, 0x27, 0x23, 0xb3, 0xce, 0xc9, 0x36, 0xa6, 0xcb, 0xae, 0x94, 0x66, 0x91, 0x5f, 0xcc, - 0xc0, 0x34, 0x8f, 0xc7, 0x57, 0x1b, 0xdb, 0x37, 0xc6, 0xba, 0xfe, 0x64, 0x1a, 0xfb, 0x9c, 0xc8, + 0x48, 0x4b, 0xd1, 0x73, 0x77, 0x4b, 0x25, 0x79, 0x67, 0x84, 0x7a, 0xa2, 0x58, 0xdc, 0xca, 0x7b, + 0x78, 0x31, 0xff, 0xca, 0x54, 0x0e, 0xc6, 0x9f, 0x64, 0x22, 0xe9, 0x42, 0x5e, 0x86, 0xbc, 0xb9, + 0x11, 0xb6, 0x9f, 0xc7, 0x5b, 0xc5, 0x9a, 0x8f, 0x08, 0xe4, 0x43, 0x38, 0xa1, 0xf0, 0x49, 0x38, + 0x7b, 0xbe, 0x84, 0xe1, 0x40, 0x4a, 0x4b, 0xd2, 0x3d, 0x3e, 0xd3, 0x79, 0xa0, 0x52, 0x1c, 0x15, + 0x54, 0x68, 0xdb, 0xe1, 0xbc, 0x95, 0xce, 0xaa, 0xbc, 0x1b, 0x88, 0x10, 0xef, 0x6c, 0x1a, 0x07, + 0x1e, 0x0d, 0x64, 0xfc, 0x46, 0x46, 0x93, 0x1a, 0xe1, 0xbb, 0x62, 0x99, 0x23, 0xde, 0x15, 0x7b, + 0x13, 0xa0, 0xd4, 0x0d, 0xdc, 0xe5, 0xb6, 0xe7, 0x36, 0xb9, 0xe5, 0x42, 0xa4, 0x62, 0x45, 0x7b, + 0x2c, 0x45, 0xb0, 0x16, 0xb4, 0x10, 0x22, 0xa7, 0xfa, 0xc5, 0xe6, 0x3e, 0xad, 0x5f, 0xac, 0xf1, + 0x7b, 0x19, 0xed, 0xbb, 0x61, 0xda, 0x9e, 0xfc, 0xf4, 0x14, 0xb7, 0x85, 0xe4, 0xa7, 0x17, 0x7d, + 0x78, 0xff, 0x6a, 0x06, 0x4e, 0x72, 0x07, 0xd3, 0xb5, 0x6e, 0x6b, 0x9b, 0x7a, 0xf7, 0xed, 0xa6, + 0xd3, 0xe0, 0x31, 0x6f, 0x5c, 0x91, 0xbd, 0x94, 0xfc, 0x08, 0xd3, 0xf1, 0xf9, 0xe1, 0x90, 0x3b, + 0xbc, 0x5a, 0x6d, 0x2c, 0xb4, 0x1e, 0x86, 0xa5, 0xea, 0xe1, 0x30, 0x9d, 0xde, 0xf8, 0x95, 0x0c, + 0xbc, 0x70, 0x64, 0x2d, 0xe4, 0x2a, 0x8c, 0xc8, 0x1c, 0xb8, 0x19, 0x1c, 0x78, 0x74, 0xf6, 0x4a, + 0xe6, 0xbf, 0x95, 0x58, 0xe4, 0xeb, 0x70, 0x42, 0x65, 0xb5, 0xe9, 0xd9, 0x8e, 0x9a, 0x69, 0x36, + 0xa5, 0xd5, 0x01, 0x43, 0x89, 0x6b, 0x5d, 0xe9, 0x4c, 0x8c, 0xff, 0x2b, 0xa3, 0xbc, 0x34, 0xf8, + 0x8c, 0xea, 0xe2, 0x37, 0x34, 0x5d, 0x5c, 0xe6, 0x23, 0x0a, 0x7b, 0xc5, 0xca, 0x52, 0xcf, 0x4f, + 0x53, 0x8a, 0xd3, 0x22, 0x02, 0xbe, 0x93, 0x85, 0xb1, 0x7b, 0x3e, 0xf5, 0xf8, 0xe5, 0xe9, 0xe7, + 0x2b, 0xef, 0x4c, 0xd8, 0xaf, 0x81, 0x32, 0x83, 0xfc, 0x51, 0x06, 0x8d, 0xea, 0x2a, 0x05, 0x1b, + 0x0d, 0xe5, 0x75, 0x11, 0x1c, 0x0d, 0x7c, 0x57, 0x04, 0xa1, 0x3c, 0x7b, 0xc8, 0xaa, 0xfe, 0xd0, + 0x10, 0xbe, 0x36, 0xb5, 0x4a, 0xde, 0x87, 0xa1, 0x7b, 0x68, 0x22, 0xd4, 0xe3, 0x9b, 0x43, 0xfe, + 0x58, 0xc8, 0x85, 0x74, 0x97, 0xfd, 0xa9, 0xee, 0x31, 0x58, 0x46, 0x6a, 0x30, 0xb2, 0xe4, 0x51, + 0x7c, 0x37, 0x30, 0x3f, 0x78, 0x74, 0x5e, 0x9d, 0x93, 0xc4, 0xa3, 0xf3, 0x04, 0x27, 0xe3, 0x67, + 0xb2, 0x40, 0xa2, 0x3e, 0x62, 0x92, 0x7c, 0xff, 0x99, 0x9d, 0xf4, 0xf7, 0xb4, 0x49, 0x3f, 0x97, + 0x98, 0x74, 0xde, 0xbd, 0x81, 0xe6, 0xfe, 0xb7, 0x32, 0x70, 0x32, 0x9d, 0x90, 0x5c, 0x80, 0xe1, + 0xf5, 0xcd, 0x0d, 0x19, 0x22, 0x2f, 0xba, 0xe2, 0x76, 0xf0, 0xcc, 0x6f, 0x8a, 0x22, 0xf2, 0x3a, + 0x0c, 0x7f, 0xd5, 0x5c, 0x62, 0xfb, 0x90, 0x92, 0xcd, 0xf5, 0x5b, 0x9e, 0x55, 0xd7, 0xb7, 0x22, + 0x81, 0xa4, 0xce, 0x6d, 0xee, 0xa9, 0xcd, 0xed, 0x4f, 0x64, 0x61, 0xaa, 0x54, 0xaf, 0x53, 0xdf, + 0x67, 0x4a, 0x0e, 0xf5, 0x83, 0x67, 0x76, 0x62, 0xd3, 0x83, 0xdf, 0xb5, 0xbe, 0x0d, 0x34, 0xab, + 0xbf, 0x93, 0x81, 0x13, 0x92, 0xea, 0xa1, 0x43, 0xf7, 0x37, 0xf7, 0x3c, 0xea, 0xef, 0xb9, 0xcd, + 0xc6, 0xc0, 0x29, 0xa3, 0x99, 0xa2, 0x87, 0x79, 0x20, 0xd5, 0x9b, 0xf4, 0x1d, 0x84, 0x68, 0x8a, + 0x1e, 0xcf, 0x15, 0x79, 0x15, 0x46, 0x4a, 0x9d, 0x8e, 0xe7, 0x3e, 0xe4, 0x9f, 0xfd, 0x84, 0x08, + 0x56, 0xe4, 0x20, 0x2d, 0xb8, 0x91, 0x83, 0x58, 0x33, 0x2a, 0xb4, 0xcd, 0xb3, 0xf5, 0x4c, 0xf0, + 0x66, 0x34, 0x68, 0x5b, 0xd5, 0x8f, 0xb1, 0xdc, 0xa8, 0x01, 0xd9, 0xf0, 0xdc, 0x96, 0x1b, 0xd0, + 0x06, 0xef, 0x0f, 0xc6, 0x84, 0x1e, 0x99, 0x66, 0x64, 0xd3, 0x09, 0x9a, 0x5a, 0x9a, 0x91, 0x80, + 0x01, 0x4c, 0x0e, 0x37, 0xfe, 0x8f, 0x21, 0x18, 0x57, 0x47, 0x87, 0x18, 0x3c, 0x0f, 0xac, 0xeb, + 0xa9, 0x81, 0xc9, 0x36, 0x42, 0x4c, 0x51, 0x12, 0x45, 0xf5, 0x67, 0x8f, 0x8c, 0xea, 0xdf, 0x82, + 0x89, 0x0d, 0xcf, 0xed, 0xb8, 0x3e, 0x6d, 0xf0, 0xf7, 0x64, 0xb9, 0x28, 0x9c, 0x51, 0xce, 0x8f, + 0x6c, 0x22, 0xf1, 0x0e, 0x12, 0xad, 0x27, 0x1d, 0x81, 0x6d, 0xc5, 0x5f, 0x9b, 0xd5, 0xf9, 0x70, + 0xf7, 0x06, 0xdb, 0x17, 0x49, 0xb9, 0x42, 0xf7, 0x06, 0x06, 0xd1, 0xdd, 0x1b, 0x18, 0x44, 0xfd, + 0xd6, 0x86, 0x9e, 0xd6, 0xb7, 0x46, 0x7e, 0x26, 0x03, 0x63, 0xa5, 0x76, 0x5b, 0x64, 0x0b, 0x38, + 0x22, 0x50, 0xf2, 0xeb, 0xc2, 0xc3, 0xe1, 0xad, 0x4f, 0xe5, 0xe1, 0x80, 0x7a, 0x8b, 0x8f, 0x9a, + 0x6a, 0x54, 0xa1, 0x7a, 0x82, 0x52, 0xda, 0x41, 0xde, 0x82, 0x62, 0xb8, 0xc8, 0xab, 0xed, 0x06, + 0x7d, 0x44, 0xfd, 0xb9, 0x91, 0xf3, 0xb9, 0x4b, 0x13, 0x22, 0x1d, 0x9f, 0xaa, 0x99, 0xc6, 0x11, + 0xc9, 0x26, 0x80, 0x1d, 0xae, 0xae, 0xd8, 0x4b, 0x3e, 0xc9, 0xe5, 0x27, 0xb4, 0x67, 0xfc, 0x8d, + 0x97, 0x48, 0xaa, 0xf6, 0x1c, 0xf1, 0x21, 0x2d, 0x98, 0xe2, 0xcf, 0xe8, 0xe0, 0xf3, 0xba, 0x98, + 0x75, 0x16, 0x8e, 0x9c, 0x87, 0x97, 0x85, 0x1d, 0xec, 0x8c, 0x78, 0x9c, 0x07, 0x5f, 0xec, 0xb5, + 0x52, 0x52, 0xd0, 0xc6, 0x79, 0xf3, 0xe4, 0x87, 0xe6, 0xa9, 0x64, 0x7b, 0xf9, 0xa2, 0xff, 0x89, + 0x0c, 0x9c, 0x54, 0x17, 0x7d, 0xad, 0xbb, 0xdd, 0x72, 0xf0, 0x2c, 0x48, 0xae, 0xc0, 0xa8, 0x58, + 0x93, 0xe1, 0x21, 0x2a, 0x99, 0x3c, 0x37, 0x42, 0x21, 0xcb, 0x6c, 0x19, 0x32, 0x1e, 0x42, 0xeb, + 0x9e, 0x89, 0xc9, 0x29, 0x56, 0x14, 0x3d, 0xd1, 0xe6, 0xe1, 0x6f, 0x7d, 0x7d, 0x32, 0x88, 0xf1, + 0x2e, 0x4c, 0xeb, 0x33, 0x51, 0xa3, 0x01, 0xb9, 0x0c, 0x23, 0x72, 0xfa, 0x32, 0xe9, 0xd3, 0x27, + 0xcb, 0x8d, 0x2d, 0x20, 0x09, 0x7a, 0x1f, 0x5d, 0x91, 0x68, 0x20, 0x5d, 0xe5, 0xe4, 0x45, 0x60, + 0x02, 0x31, 0x7c, 0xb3, 0x7c, 0x4c, 0xf3, 0x8d, 0x65, 0xa4, 0xc6, 0x9f, 0x4c, 0xc2, 0x4c, 0x8a, + 0xcc, 0x3d, 0x42, 0x27, 0x5a, 0xd0, 0x05, 0xc4, 0x68, 0x18, 0x67, 0x2d, 0xc5, 0xc2, 0xbb, 0xf2, + 0x79, 0xe9, 0x3e, 0xe2, 0xa0, 0xdf, 0x9b, 0xd3, 0x9f, 0x85, 0x5e, 0xa4, 0xa6, 0x42, 0x18, 0x7a, + 0x6a, 0xa9, 0x10, 0xca, 0x30, 0x21, 0x7a, 0x25, 0xc4, 0xd5, 0x70, 0x64, 0x29, 0xf6, 0x78, 0x81, + 0x95, 0x10, 0x5b, 0x3a, 0x09, 0xe7, 0xe1, 0xbb, 0xcd, 0x87, 0x54, 0xf0, 0x18, 0x51, 0x79, 0x60, + 0x41, 0x2a, 0x0f, 0x85, 0x84, 0xfc, 0x7b, 0xf8, 0x12, 0x08, 0x42, 0x54, 0x99, 0x55, 0xe8, 0x27, + 0xb3, 0x1a, 0x4f, 0x47, 0x66, 0x9d, 0x93, 0x6d, 0x4c, 0x97, 0x5d, 0x29, 0xcd, 0x22, 0xbf, 0x94, + 0x81, 0x69, 0x1e, 0x8f, 0xaf, 0x36, 0xb6, 0x6f, 0x8c, 0x75, 0xfd, 0xe9, 0x34, 0xf6, 0xac, 0xc8, 0x80, 0x9f, 0xde, 0xd6, 0x64, 0xa3, 0xc8, 0xf7, 0x00, 0x84, 0x5f, 0x94, 0x3f, 0x07, 0xf8, 0xa9, - 0x3d, 0x97, 0x22, 0x05, 0x42, 0xa4, 0x28, 0x5b, 0x6f, 0x10, 0xd2, 0x69, 0xef, 0xbf, 0x84, 0x50, - 0xf2, 0xfd, 0x30, 0xcb, 0xbe, 0x97, 0x10, 0x22, 0xb2, 0x87, 0xcc, 0x8d, 0x61, 0x2d, 0x9f, 0xef, - 0xad, 0x13, 0x5d, 0x49, 0x23, 0xe3, 0xb9, 0x05, 0xa3, 0x37, 0xf4, 0x02, 0x35, 0xd0, 0x38, 0xb5, - 0x22, 0x4c, 0xca, 0x83, 0xad, 0xe7, 0x19, 0x75, 0x7b, 0xc8, 0xb7, 0xb3, 0xf2, 0x5b, 0xe0, 0xf2, - 0xcd, 0xd7, 0x03, 0xe5, 0x10, 0x44, 0xbe, 0x0c, 0x24, 0x0c, 0x64, 0xe7, 0x30, 0x2a, 0xb3, 0xed, - 0x72, 0xb3, 0x71, 0x14, 0x10, 0xef, 0xc9, 0x62, 0x75, 0x91, 0x24, 0x89, 0x09, 0x85, 0x59, 0xd1, - 0x69, 0x06, 0x95, 0xcf, 0x74, 0xf8, 0x73, 0x93, 0x5a, 0x6e, 0x96, 0xa8, 0x24, 0x7a, 0x6c, 0x4f, - 0x79, 0xeb, 0x43, 0x33, 0x39, 0xa5, 0xb1, 0x23, 0x37, 0x60, 0x14, 0xa3, 0xd5, 0x56, 0xa4, 0x83, - 0x95, 0x70, 0xf6, 0xc0, 0xb8, 0x36, 0x6b, 0x4f, 0x77, 0x93, 0x8a, 0x50, 0xd9, 0x71, 0xa0, 0xe2, - 0x1d, 0x9a, 0xdd, 0x36, 0x1a, 0x77, 0x85, 0xbd, 0xa3, 0xe1, 0x1d, 0x5a, 0x5e, 0x57, 0x0f, 0x67, - 0x44, 0x24, 0xf2, 0x0d, 0x18, 0xbb, 0x6b, 0x3f, 0x94, 0xb6, 0x5d, 0x61, 0xc0, 0x1d, 0xe8, 0x99, - 0xf8, 0x96, 0xfd, 0xd0, 0x6a, 0x74, 0xe3, 0x99, 0x0d, 0xf9, 0x33, 0xf1, 0x0a, 0x4b, 0xf2, 0x35, - 0x00, 0xc5, 0xe2, 0x4c, 0x8e, 0xad, 0xe0, 0x05, 0x99, 0x71, 0x28, 0xd5, 0x12, 0x8d, 0xfc, 0x15, - 0x86, 0x31, 0xcd, 0x61, 0xf6, 0xd3, 0xd3, 0x1c, 0x4e, 0x7d, 0x7a, 0x9a, 0xc3, 0xfc, 0x36, 0x9c, - 0xed, 0xf9, 0xe9, 0xa4, 0x24, 0x82, 0xbc, 0xaa, 0x27, 0x82, 0x3c, 0xdb, 0x6b, 0x8b, 0xf5, 0xf5, - 0x04, 0xcd, 0x33, 0xc5, 0xd9, 0xde, 0xda, 0xc9, 0x77, 0xb3, 0xb1, 0x2d, 0x57, 0x1c, 0x2c, 0x78, - 0x42, 0xff, 0x5e, 0x3a, 0x49, 0x16, 0x1f, 0x5f, 0xe3, 0x9b, 0x72, 0x36, 0x3a, 0xd0, 0xc4, 0xde, - 0xab, 0xe5, 0xdb, 0xf3, 0xe3, 0xee, 0xbe, 0x6f, 0xc3, 0x24, 0x7f, 0x76, 0xe9, 0x0e, 0x3d, 0x3c, - 0x70, 0xbd, 0x86, 0x7c, 0x94, 0x14, 0x75, 0xf0, 0xc4, 0x63, 0x87, 0x31, 0x5c, 0x52, 0x91, 0x01, - 0x50, 0x43, 0x58, 0xfb, 0xd9, 0x54, 0x29, 0xc6, 0x10, 0xfa, 0xc5, 0x46, 0x91, 0x37, 0x42, 0x45, - 0x8d, 0x7a, 0x6a, 0x9a, 0x66, 0x4f, 0x02, 0x53, 0xf4, 0x35, 0xea, 0x19, 0xbf, 0x9f, 0x03, 0xc2, - 0x6b, 0x2a, 0xdb, 0x1d, 0x1b, 0xc3, 0x03, 0x1d, 0x4c, 0x73, 0x51, 0x14, 0x38, 0xf6, 0x76, 0x93, - 0xaa, 0x39, 0x62, 0x84, 0x43, 0x6b, 0x58, 0x66, 0xc5, 0x0f, 0x3a, 0x09, 0xc2, 0x1e, 0xa2, 0x2e, - 0xfb, 0x38, 0xa2, 0xee, 0x1b, 0xf0, 0x6c, 0xa9, 0x83, 0xef, 0xb7, 0xc9, 0x5a, 0x6e, 0xba, 0x9e, - 0x14, 0x52, 0x5a, 0xe0, 0x89, 0x1d, 0xa2, 0x25, 0x5a, 0xda, 0x8f, 0x85, 0xa2, 0xa7, 0xb0, 0x75, - 0xd9, 0x09, 0xd4, 0x40, 0x66, 0xa9, 0xa7, 0x74, 0xb0, 0x24, 0x45, 0x4f, 0xe1, 0x24, 0x92, 0x87, - 0xe3, 0x49, 0x3d, 0x05, 0x1f, 0x26, 0x88, 0x78, 0x38, 0x1e, 0xed, 0xa1, 0xeb, 0x84, 0x24, 0xe4, - 0x6d, 0x18, 0x2b, 0x75, 0x03, 0x57, 0x30, 0x16, 0x9e, 0xd8, 0x91, 0xcf, 0xb4, 0x68, 0x8a, 0x76, - 0xf4, 0x89, 0xd0, 0x8d, 0x3f, 0xca, 0xc1, 0xd9, 0xe4, 0xf4, 0x8a, 0xd2, 0xf0, 0xfb, 0xc8, 0x1c, - 0xf3, 0x7d, 0xa4, 0xad, 0x06, 0x7e, 0x59, 0xf0, 0xc4, 0x56, 0x03, 0x7f, 0x06, 0xee, 0x13, 0xae, - 0x86, 0x1a, 0x8c, 0xa9, 0xfb, 0x5d, 0xfe, 0x93, 0xee, 0x77, 0x2a, 0x17, 0x76, 0xa8, 0xe7, 0xf1, - 0xdb, 0x43, 0xd1, 0xd5, 0x51, 0x3c, 0x74, 0x9b, 0x63, 0x90, 0x7f, 0x05, 0xce, 0x73, 0x99, 0x14, - 0xef, 0xec, 0xd2, 0xa1, 0xe4, 0x28, 0x26, 0x6e, 0xf1, 0xd1, 0xd1, 0xc2, 0x15, 0x6e, 0x2a, 0xb1, - 0x12, 0xc3, 0x66, 0x6d, 0x1f, 0x5a, 0xb2, 0x65, 0x4a, 0x25, 0xc7, 0xf2, 0xc6, 0xb7, 0xdf, 0x94, - 0x77, 0xb9, 0x5e, 0x4f, 0x0b, 0x51, 0xe1, 0xb9, 0x4e, 0x39, 0x58, 0x8f, 0x4e, 0x91, 0xe6, 0xb0, - 0x6c, 0xaa, 0x39, 0x4c, 0xda, 0x53, 0x72, 0xa9, 0xf6, 0x94, 0x0a, 0x4c, 0xd5, 0xba, 0xdb, 0xb2, - 0x6e, 0x44, 0xcc, 0x6b, 0x51, 0x76, 0x69, 0x1d, 0x8a, 0x93, 0x18, 0x3f, 0x92, 0x85, 0xf1, 0x8d, - 0x66, 0x77, 0xd7, 0x69, 0x57, 0xec, 0xc0, 0x7e, 0x6a, 0x2d, 0x74, 0x6f, 0x6a, 0x16, 0xba, 0x30, - 0x12, 0x2b, 0xec, 0xd8, 0x40, 0xe6, 0xb9, 0x9f, 0xca, 0xc0, 0x54, 0x44, 0xc2, 0xf7, 0xd9, 0x15, - 0xc8, 0xb3, 0x1f, 0xe2, 0xdc, 0x7a, 0x3e, 0xc1, 0x98, 0x3f, 0x06, 0x13, 0xfe, 0x25, 0x6c, 0x66, - 0xfa, 0x4b, 0x0b, 0xc8, 0x61, 0xfe, 0x0b, 0x30, 0x1a, 0xb1, 0x3d, 0xc9, 0x23, 0x30, 0xbf, 0x92, - 0x81, 0x62, 0xbc, 0x27, 0xe4, 0x0e, 0x8c, 0x30, 0x4e, 0x0e, 0x95, 0x47, 0xea, 0x17, 0x7b, 0xf4, - 0xf9, 0x8a, 0x40, 0xe3, 0xcd, 0xc3, 0xc1, 0xa7, 0x1c, 0x62, 0x4a, 0x0e, 0xf3, 0x26, 0x8c, 0xab, - 0x58, 0x29, 0xad, 0x7b, 0x4d, 0x57, 0x2e, 0x4e, 0xa7, 0x8f, 0x83, 0xf6, 0x74, 0x8d, 0xd6, 0x6a, - 0xa1, 0x37, 0x5c, 0xd4, 0x16, 0x17, 0x8e, 0x55, 0x6c, 0xdd, 0xf0, 0x65, 0xb6, 0x18, 0xa5, 0x5f, - 0x56, 0xd7, 0x59, 0xca, 0x82, 0x0e, 0xf1, 0xc8, 0x6b, 0x30, 0xcc, 0xeb, 0x53, 0x9f, 0x70, 0xe8, - 0x20, 0x44, 0x55, 0x71, 0x39, 0x8e, 0xf1, 0xb7, 0x73, 0x70, 0x3a, 0x6a, 0xde, 0xbd, 0x4e, 0xc3, - 0x0e, 0xe8, 0x86, 0xed, 0xd9, 0x2d, 0xff, 0x98, 0x2f, 0xe0, 0x52, 0xa2, 0x69, 0x98, 0xd2, 0x5f, - 0x36, 0x4d, 0x69, 0x90, 0x11, 0x6b, 0x10, 0x9a, 0x2f, 0x79, 0x83, 0x64, 0x33, 0xc8, 0x1d, 0xc8, - 0xd5, 0x68, 0x20, 0xc4, 0xe6, 0xc5, 0xc4, 0xa8, 0xaa, 0xed, 0xba, 0x52, 0xa3, 0x01, 0x9f, 0x44, - 0x9e, 0x57, 0x84, 0x6a, 0x79, 0x1d, 0x6b, 0x34, 0x20, 0x5b, 0x30, 0xbc, 0xfc, 0xb0, 0x43, 0xeb, - 0x81, 0x78, 0xc2, 0xe8, 0x72, 0x7f, 0x7e, 0x1c, 0x57, 0x79, 0xc1, 0x88, 0x22, 0x40, 0x1d, 0x2c, - 0x8e, 0x32, 0x7f, 0x03, 0x0a, 0xb2, 0xf2, 0x93, 0xac, 0xdc, 0xf9, 0x37, 0x61, 0x4c, 0xa9, 0xe4, - 0x44, 0x8b, 0xfe, 0xe7, 0x98, 0x5c, 0x75, 0x9b, 0xf2, 0xd5, 0xa3, 0xe5, 0x84, 0x9a, 0x97, 0x89, - 0xe2, 0x81, 0xb9, 0x9a, 0x67, 0xed, 0x8b, 0xa2, 0x3e, 0xfa, 0x5e, 0x15, 0xa6, 0x6a, 0xfb, 0x4e, - 0x27, 0x4a, 0xaf, 0xa7, 0x6d, 0xa6, 0x98, 0x8f, 0x5e, 0x9c, 0xb9, 0xe3, 0x9b, 0x69, 0x9c, 0xce, - 0xf8, 0xd3, 0x0c, 0x0c, 0xb3, 0xbf, 0xee, 0xdf, 0x78, 0x4a, 0x45, 0xe6, 0x75, 0x4d, 0x64, 0x4e, - 0x2b, 0x19, 0x6e, 0x51, 0x70, 0xdc, 0x38, 0x46, 0x58, 0x1e, 0x89, 0x09, 0xe2, 0xc8, 0xe4, 0x16, - 0x8c, 0x08, 0xaf, 0x1e, 0xe1, 0x7e, 0xad, 0xa6, 0xcc, 0x95, 0xfe, 0x3e, 0xe1, 0xe1, 0xdc, 0xed, - 0xc4, 0xad, 0x19, 0x92, 0x9a, 0xa9, 0xe4, 0x32, 0xd1, 0xa1, 0xf6, 0x56, 0x9e, 0x8b, 0xb1, 0x6d, - 0x3c, 0xe1, 0xab, 0xf2, 0xba, 0x65, 0x8f, 0xa4, 0x01, 0x25, 0x71, 0x91, 0x91, 0xeb, 0xc7, 0xe4, - 0xb4, 0x7c, 0x4a, 0x2c, 0xf5, 0x8e, 0xe3, 0x0f, 0x66, 0x78, 0x9a, 0x54, 0xd9, 0xb0, 0x77, 0x60, - 0xfc, 0xa6, 0xeb, 0x1d, 0xd8, 0x1e, 0x4f, 0x7e, 0x27, 0x3c, 0x07, 0xd8, 0xd1, 0x71, 0x62, 0x87, - 0xc3, 0x79, 0xfa, 0xbc, 0x8f, 0x8f, 0x16, 0xf2, 0x4b, 0xae, 0xdb, 0x34, 0x35, 0x74, 0xb2, 0x0e, - 0x13, 0x77, 0xed, 0x87, 0xca, 0xa1, 0x97, 0x07, 0xab, 0x5c, 0x66, 0x0b, 0x98, 0x9d, 0x9a, 0x8f, - 0x77, 0xb1, 0xd2, 0xe9, 0x89, 0x03, 0x93, 0x1b, 0xae, 0x17, 0x88, 0x4a, 0x9c, 0xf6, 0xae, 0xe8, - 0x6c, 0xd2, 0x49, 0xec, 0x6a, 0xaa, 0x93, 0xd8, 0xd9, 0x8e, 0xeb, 0x05, 0xd6, 0x4e, 0x48, 0xae, - 0x25, 0xe4, 0xd1, 0x18, 0x93, 0x77, 0x60, 0x5a, 0x49, 0x38, 0x76, 0xd3, 0xf5, 0x5a, 0xb6, 0x54, - 0xca, 0xd1, 0x0e, 0x8c, 0xfe, 0x26, 0x3b, 0x08, 0x36, 0x93, 0x98, 0xe4, 0xc3, 0xb4, 0xf0, 0x9f, - 0xa1, 0xc8, 0xcb, 0x2c, 0x25, 0xfc, 0xa7, 0x97, 0x97, 0x59, 0x32, 0x10, 0x68, 0xb7, 0x9f, 0x17, - 0x6a, 0x61, 0xe9, 0x9a, 0x38, 0x7e, 0x1f, 0xef, 0x65, 0x1a, 0xce, 0x5b, 0x0f, 0x6f, 0xd3, 0x45, - 0xc8, 0x2d, 0x6d, 0xdc, 0xc4, 0xdb, 0x0b, 0xe9, 0x68, 0xd3, 0xde, 0xb3, 0xdb, 0x75, 0x54, 0x96, - 0x85, 0xe7, 0xb7, 0x2a, 0x91, 0x97, 0x36, 0x6e, 0x12, 0x1b, 0x66, 0x36, 0xa8, 0xd7, 0x72, 0x82, - 0xaf, 0x5c, 0xbb, 0xa6, 0x4c, 0x54, 0x01, 0x9b, 0x76, 0x55, 0x34, 0x6d, 0xa1, 0x83, 0x28, 0xd6, - 0xc3, 0x6b, 0xd7, 0x52, 0xa7, 0x23, 0x6c, 0x58, 0x1a, 0x2f, 0x26, 0x19, 0xef, 0xda, 0x0f, 0x23, - 0x87, 0x7d, 0x5f, 0x04, 0x52, 0x9e, 0x93, 0x0b, 0x2b, 0x72, 0xf6, 0xd7, 0x24, 0xa3, 0x4e, 0xc4, - 0xce, 0x3a, 0xd1, 0xf2, 0xf2, 0x45, 0x08, 0xca, 0xbc, 0x34, 0xe9, 0xc8, 0x68, 0x5b, 0x55, 0x61, - 0x57, 0xd0, 0xc9, 0xbd, 0xf0, 0xc4, 0xc6, 0x4f, 0x3c, 0xe2, 0xa1, 0xac, 0xab, 0xea, 0x89, 0x8d, - 0x1b, 0x52, 0xb4, 0x6e, 0x4d, 0x85, 0xc7, 0x7c, 0x1e, 0xc1, 0x60, 0xea, 0x5c, 0x92, 0x07, 0xc1, - 0xf1, 0x93, 0x1f, 0x04, 0x29, 0xe4, 0x57, 0xdd, 0xfa, 0xbe, 0xc8, 0x22, 0xf4, 0x65, 0xf6, 0xb9, - 0x37, 0xdd, 0xfa, 0xfe, 0x93, 0xf3, 0xae, 0x45, 0xf6, 0x64, 0x8d, 0x35, 0x95, 0xad, 0x02, 0x31, - 0x26, 0xc2, 0x63, 0x73, 0x36, 0x3c, 0x09, 0x29, 0x65, 0x5c, 0xf1, 0xe1, 0x8b, 0x46, 0x0e, 0xad, - 0xa9, 0x93, 0x13, 0x0a, 0xc5, 0x0a, 0xf5, 0xf7, 0x03, 0xb7, 0x53, 0x6e, 0x3a, 0x9d, 0x6d, 0xd7, - 0xf6, 0x1a, 0x68, 0xbb, 0x4b, 0xfb, 0xbe, 0x5f, 0x4e, 0xfd, 0xbe, 0xa7, 0x1b, 0x9c, 0xde, 0xaa, - 0x4b, 0x06, 0x66, 0x82, 0x25, 0xf9, 0x10, 0x26, 0xd9, 0xe2, 0x5e, 0x7e, 0x18, 0xd0, 0x36, 0x9f, - 0xf9, 0x69, 0x54, 0x1d, 0x66, 0x95, 0xd4, 0xe2, 0x61, 0x21, 0x5f, 0x53, 0xf8, 0xb1, 0xd3, 0x90, - 0x40, 0xcb, 0xc0, 0xa4, 0xb1, 0x22, 0x0d, 0x98, 0xbb, 0x6b, 0x3f, 0x54, 0x9e, 0xf7, 0x52, 0x16, - 0x29, 0xc1, 0x05, 0x86, 0x2f, 0xb2, 0xb3, 0x05, 0x16, 0x25, 0xff, 0xec, 0xb1, 0x5e, 0x7b, 0x72, - 0x22, 0xdf, 0x0b, 0x67, 0x44, 0xb7, 0x2a, 0xf8, 0xde, 0x86, 0xeb, 0x1d, 0xd6, 0xf6, 0x6c, 0x8c, - 0xd5, 0x99, 0x39, 0x99, 0x40, 0x94, 0x03, 0xd6, 0x90, 0x7c, 0x2c, 0x9f, 0x33, 0x32, 0x7b, 0xd5, - 0x40, 0x3e, 0x82, 0x49, 0x7e, 0x65, 0xb3, 0xe2, 0xfa, 0x01, 0x1e, 0xe8, 0x67, 0x4f, 0xe6, 0x82, - 0xce, 0xef, 0x81, 0x78, 0xd0, 0x46, 0xcc, 0x00, 0x10, 0xe3, 0x4c, 0xde, 0x82, 0xb1, 0x0d, 0xa7, - 0xcd, 0x73, 0xa4, 0x55, 0x37, 0xd0, 0xf4, 0x28, 0xf6, 0x9f, 0x8e, 0xd3, 0xb6, 0xe4, 0xa9, 0xba, - 0x13, 0x8a, 0x0b, 0x15, 0x9b, 0x6c, 0xc1, 0x58, 0xad, 0xb6, 0x72, 0xd3, 0x61, 0x1b, 0x60, 0xe7, - 0x70, 0xee, 0x74, 0x8f, 0x56, 0x5e, 0x48, 0x6d, 0xe5, 0x84, 0xef, 0xef, 0xe1, 0x93, 0xc9, 0x56, - 0xdd, 0xed, 0x1c, 0x9a, 0x2a, 0xa7, 0x14, 0xb7, 0xec, 0x33, 0x4f, 0xd8, 0x2d, 0xbb, 0x0a, 0x53, - 0x8a, 0x83, 0x25, 0x3a, 0x57, 0xce, 0x45, 0x29, 0xc1, 0x54, 0x37, 0xec, 0x78, 0xc8, 0x60, 0x9c, - 0x4e, 0xfa, 0x63, 0x9f, 0x3d, 0xa9, 0x3f, 0xb6, 0x03, 0xd3, 0x7c, 0x32, 0xc4, 0x3a, 0xc0, 0x99, - 0x9e, 0xef, 0x31, 0x86, 0x97, 0x53, 0xc7, 0x70, 0x46, 0xcc, 0xb4, 0x5c, 0x64, 0x78, 0x45, 0x99, - 0xe4, 0x4a, 0x76, 0x80, 0x08, 0xa0, 0x78, 0xb0, 0x19, 0xeb, 0x7a, 0xb6, 0x47, 0x5d, 0x2f, 0xa6, - 0xd6, 0x35, 0x29, 0xeb, 0xda, 0xe6, 0xd5, 0xa4, 0x70, 0x24, 0x6d, 0x59, 0x8f, 0x5c, 0x5f, 0x38, - 0xb0, 0xcf, 0x69, 0x76, 0xd0, 0x24, 0x02, 0x4f, 0x50, 0x1a, 0x5f, 0xb4, 0xf1, 0x71, 0x4f, 0xe1, - 0x4c, 0x1e, 0xc2, 0xe9, 0x64, 0x2b, 0xb0, 0xce, 0x73, 0x58, 0xe7, 0x39, 0xad, 0xce, 0x38, 0x12, - 0x5f, 0x37, 0x7a, 0xb7, 0xe2, 0xb5, 0xf6, 0xe0, 0x7f, 0x3b, 0x5f, 0x98, 0x28, 0x4e, 0xa6, 0x79, - 0x5a, 0xff, 0x46, 0x36, 0x26, 0xb4, 0x49, 0x15, 0x46, 0xc4, 0x5c, 0x08, 0x2d, 0x36, 0x39, 0xe2, - 0xe7, 0x52, 0x47, 0x7c, 0x44, 0x4c, 0xab, 0x29, 0xe9, 0xc9, 0x01, 0x63, 0x85, 0x2e, 0xf1, 0x42, - 0xed, 0xff, 0x1a, 0x97, 0xc9, 0x08, 0xd2, 0x76, 0x9f, 0xca, 0xc9, 0x03, 0x82, 0xf4, 0x78, 0x33, - 0xdc, 0x86, 0x64, 0x6d, 0x64, 0x9f, 0xbf, 0x42, 0x90, 0x0b, 0xa3, 0x4a, 0xf4, 0x27, 0x07, 0x9e, - 0x58, 0x85, 0xac, 0x16, 0xe3, 0xd7, 0x33, 0x30, 0xa1, 0x49, 0x7d, 0x72, 0x43, 0x09, 0x99, 0x8a, - 0x22, 0x7e, 0x35, 0x1c, 0x14, 0x04, 0xf1, 0x60, 0xaa, 0x1b, 0xc2, 0x6f, 0x3a, 0xdb, 0x9b, 0x2e, - 0xf5, 0xdd, 0xf2, 0xfe, 0x46, 0xb2, 0xf0, 0x6d, 0xa3, 0x7c, 0x8f, 0xb7, 0x8d, 0x7e, 0xf5, 0x59, - 0x98, 0xd4, 0x8f, 0x05, 0xe4, 0x35, 0x18, 0x46, 0xdb, 0xa2, 0x3c, 0x63, 0xf2, 0xd7, 0x7d, 0x11, - 0xa2, 0xbd, 0xee, 0x8b, 0x10, 0xf2, 0x12, 0x40, 0xe8, 0xc0, 0x2a, 0x2d, 0xeb, 0x43, 0x8f, 0x8e, - 0x16, 0x32, 0xaf, 0x9b, 0x4a, 0x01, 0xf9, 0x3a, 0xc0, 0x9a, 0xdb, 0xa0, 0xe1, 0xfb, 0x6b, 0x7d, - 0x6e, 0x8f, 0x5f, 0x4e, 0x64, 0xe8, 0x3e, 0xd5, 0x76, 0x1b, 0x34, 0x99, 0x8e, 0x5b, 0xe1, 0x48, - 0xbe, 0x04, 0x43, 0x66, 0x97, 0x9d, 0x67, 0xb9, 0x29, 0x61, 0x4c, 0x4a, 0xdf, 0x6e, 0x93, 0x46, - 0x87, 0x25, 0xaf, 0x1b, 0x77, 0x8c, 0x62, 0x00, 0xf2, 0x1e, 0xcf, 0xdc, 0x2d, 0x12, 0x6d, 0x0d, - 0x45, 0x77, 0x0d, 0xca, 0xae, 0x9c, 0x48, 0xb5, 0xa5, 0x90, 0x90, 0x75, 0x18, 0x51, 0x8d, 0xe4, - 0x4a, 0xec, 0xad, 0x7a, 0x91, 0xa2, 0x9c, 0xbc, 0xc4, 0xc3, 0x6d, 0x71, 0xfb, 0xb9, 0xe4, 0x42, - 0xde, 0x86, 0x51, 0xc6, 0x9e, 0x7d, 0xc2, 0xbe, 0xd0, 0xb8, 0xf1, 0x46, 0x41, 0x69, 0x10, 0x93, - 0x00, 0x5a, 0x3a, 0xac, 0x90, 0x80, 0x7c, 0x88, 0x6f, 0x93, 0x89, 0xa1, 0xee, 0xeb, 0x55, 0x70, - 0x31, 0x31, 0xd4, 0xf8, 0x58, 0x59, 0xf2, 0xd9, 0xda, 0x90, 0x1f, 0xd9, 0x0d, 0xd3, 0x34, 0x0d, - 0x92, 0x6d, 0xfd, 0x95, 0x44, 0x05, 0x73, 0x32, 0xf3, 0x50, 0xf2, 0x1d, 0x3d, 0x8d, 0x2f, 0xe9, - 0x40, 0x31, 0x52, 0x78, 0x44, 0x5d, 0xd0, 0xaf, 0xae, 0xd7, 0x13, 0x75, 0xa9, 0x13, 0x98, 0xa8, - 0x2e, 0xc1, 0x9d, 0x34, 0x60, 0x52, 0x0a, 0x4f, 0x51, 0xdf, 0x58, 0xbf, 0xfa, 0x5e, 0x4a, 0xd4, - 0x37, 0xd3, 0xd8, 0x4e, 0xd6, 0x13, 0xe3, 0x49, 0xde, 0x86, 0x09, 0x09, 0xc1, 0xef, 0x43, 0xbc, - 0x8f, 0x8b, 0x56, 0x91, 0xc6, 0x36, 0xba, 0xcc, 0xeb, 0xaf, 0x0b, 0xaa, 0xc8, 0x2a, 0x35, 0x5f, - 0x1d, 0x13, 0x1a, 0x75, 0x7c, 0x55, 0xe8, 0xc8, 0xe4, 0x03, 0x18, 0xab, 0xb6, 0x58, 0x47, 0xdc, - 0xb6, 0x1d, 0x50, 0x11, 0x97, 0x25, 0x3d, 0x24, 0x94, 0x12, 0x65, 0xa9, 0xf2, 0xf7, 0xfa, 0xa2, - 0x22, 0xed, 0xbd, 0xbe, 0x08, 0xcc, 0x06, 0x8f, 0xdf, 0x8a, 0x88, 0x35, 0x2c, 0x63, 0xb6, 0xce, - 0xa5, 0x78, 0x29, 0x28, 0xec, 0x45, 0x42, 0x3b, 0x06, 0x95, 0xb7, 0x12, 0xb1, 0x84, 0x76, 0x2a, - 0x4f, 0xf2, 0x0e, 0x8c, 0x89, 0x87, 0x28, 0x4a, 0xe6, 0x9a, 0x3f, 0x57, 0xc4, 0xce, 0x63, 0xa4, - 0xb9, 0x7c, 0xb3, 0xc2, 0xb2, 0xbd, 0x98, 0x3b, 0x5e, 0x84, 0x4f, 0xbe, 0x02, 0xb3, 0x5b, 0x4e, - 0xbb, 0xe1, 0x1e, 0xf8, 0x62, 0x9b, 0x12, 0x82, 0x6e, 0x3a, 0x0a, 0x86, 0x39, 0xe0, 0xe5, 0xa1, - 0x9e, 0x92, 0x10, 0x7c, 0xa9, 0x1c, 0xc8, 0x5f, 0x4a, 0x70, 0xe6, 0x2b, 0x88, 0xf4, 0x5b, 0x41, - 0x8b, 0x89, 0x15, 0x94, 0xac, 0x3e, 0xbe, 0x9c, 0x52, 0xab, 0x21, 0x2e, 0x10, 0x7d, 0x7f, 0xbf, - 0xed, 0x3a, 0xed, 0xb9, 0x19, 0x94, 0x85, 0xcf, 0xc6, 0x63, 0xbb, 0x11, 0x6f, 0xc3, 0x6d, 0x3a, - 0xf5, 0x43, 0xfe, 0x36, 0x7e, 0x5c, 0x1f, 0xfd, 0xc8, 0xd5, 0x6c, 0xc6, 0x29, 0xac, 0xc9, 0x07, - 0x30, 0xce, 0xfe, 0x0f, 0x0f, 0xcc, 0xb3, 0x9a, 0x5f, 0x9b, 0x82, 0x29, 0xea, 0xc1, 0x39, 0xc2, - 0x97, 0x32, 0x52, 0xce, 0xd2, 0x1a, 0x2b, 0xf2, 0x26, 0x00, 0xd3, 0x9c, 0x84, 0x38, 0x3e, 0x15, - 0xe5, 0x0f, 0x44, 0x7d, 0x2b, 0x29, 0x88, 0x23, 0x64, 0x76, 0x8a, 0x67, 0xbf, 0x6a, 0xdd, 0x86, - 0xcb, 0xbe, 0x8d, 0xd3, 0x48, 0xcb, 0xc3, 0xdd, 0x18, 0xad, 0xcf, 0xe1, 0x5a, 0xb8, 0x5b, 0x84, - 0x4e, 0x56, 0x60, 0x0a, 0xf3, 0x3c, 0x56, 0x1b, 0xb4, 0x1d, 0xe0, 0x6d, 0xe5, 0xdc, 0x19, 0xe5, - 0x36, 0x97, 0x15, 0x59, 0x4e, 0x58, 0xa6, 0xea, 0xd9, 0x31, 0x32, 0xe2, 0xc3, 0x4c, 0x24, 0x5d, - 0xa2, 0xbb, 0xe1, 0x39, 0x1c, 0x24, 0xa9, 0x5d, 0x26, 0x31, 0xb8, 0x3c, 0x66, 0x33, 0xa2, 0x08, - 0x2e, 0x69, 0x59, 0x57, 0x2b, 0x4c, 0xe3, 0x4e, 0x4c, 0x20, 0xb7, 0xca, 0x1b, 0xf1, 0x44, 0x88, - 0x67, 0xb1, 0x07, 0x38, 0xcd, 0xbb, 0xf5, 0xe8, 0x65, 0xc8, 0x94, 0x64, 0x88, 0x29, 0xd4, 0xe4, - 0x5b, 0x70, 0x4a, 0x4a, 0x10, 0x51, 0x24, 0xd6, 0xf5, 0xfc, 0x09, 0x25, 0x71, 0x63, 0x3b, 0xac, - 0x3a, 0xb1, 0xa4, 0xd3, 0xab, 0x20, 0x36, 0x8c, 0xe1, 0xb4, 0x8a, 0x1a, 0x9f, 0xed, 0x57, 0xe3, - 0xa5, 0x44, 0x8d, 0xa7, 0x71, 0xa1, 0x24, 0x2b, 0x53, 0x79, 0x92, 0x25, 0x98, 0x10, 0xdf, 0x91, - 0x58, 0x6d, 0xcf, 0xe1, 0x68, 0xa1, 0x81, 0x45, 0x7e, 0x81, 0x89, 0x05, 0xa7, 0x93, 0xa8, 0x12, - 0x99, 0x5b, 0xd4, 0xcf, 0x69, 0x12, 0x39, 0x6e, 0x48, 0xd7, 0x91, 0x99, 0x44, 0x8a, 0xb4, 0x98, - 0xe5, 0x87, 0x1d, 0x4f, 0x98, 0x4f, 0x9e, 0x8f, 0xde, 0x07, 0x50, 0x94, 0x1f, 0x8b, 0x86, 0x18, - 0xaa, 0x48, 0x48, 0xe3, 0x40, 0xee, 0xc1, 0x4c, 0xb8, 0x6b, 0x2b, 0x8c, 0x17, 0xa2, 0x77, 0x16, - 0xa2, 0xad, 0x3e, 0x9d, 0x6f, 0x1a, 0x3d, 0xb1, 0xe1, 0x8c, 0xb6, 0x4f, 0x2b, 0xac, 0xcf, 0x23, - 0x6b, 0x7c, 0x89, 0x54, 0xdf, 0xe4, 0xd3, 0xd9, 0xf7, 0xe2, 0x43, 0x3e, 0x82, 0xf9, 0xf8, 0xde, - 0xac, 0xd4, 0xf2, 0x02, 0xd6, 0xf2, 0xca, 0xa3, 0xa3, 0x85, 0x8b, 0x89, 0xed, 0x3d, 0xbd, 0xa2, - 0x3e, 0xdc, 0xc8, 0xd7, 0x61, 0x4e, 0xdf, 0x9f, 0x95, 0x9a, 0x0c, 0xac, 0x09, 0x3f, 0x9d, 0x70, - 0x63, 0x4f, 0xaf, 0xa1, 0x27, 0x0f, 0x12, 0xc0, 0x42, 0xea, 0xea, 0x56, 0xaa, 0xb9, 0x10, 0x75, - 0x28, 0xf1, 0x95, 0xa4, 0x57, 0x77, 0x1c, 0x4b, 0x72, 0x00, 0xcf, 0xa7, 0x6d, 0x13, 0x4a, 0xa5, - 0x2f, 0x86, 0x06, 0xca, 0x57, 0xd3, 0xb7, 0x9c, 0xf4, 0x9a, 0x8f, 0x61, 0x4b, 0x3e, 0x84, 0x53, - 0xca, 0xf7, 0xa5, 0xd4, 0xf7, 0x12, 0xd6, 0x87, 0xa1, 0xac, 0xea, 0x87, 0x99, 0x5e, 0x4b, 0x3a, - 0x0f, 0xd2, 0x82, 0x19, 0xd9, 0x71, 0xb4, 0x04, 0x8b, 0xad, 0xe7, 0xa2, 0x26, 0x55, 0x93, 0x18, - 0xca, 0x13, 0xce, 0xdb, 0x56, 0x27, 0x22, 0x54, 0x57, 0x7a, 0x0a, 0x5f, 0xb2, 0x02, 0xc3, 0xb5, - 0x8d, 0xea, 0xcd, 0x9b, 0xcb, 0x73, 0x2f, 0x63, 0x0d, 0x32, 0xee, 0x85, 0x03, 0xb5, 0x43, 0x93, - 0x70, 0xb7, 0xea, 0x38, 0x3b, 0x3b, 0x5a, 0x78, 0x11, 0x47, 0xbd, 0x9d, 0x2f, 0x5c, 0x2a, 0x5e, - 0xbe, 0x9d, 0x2f, 0x5c, 0x2e, 0xbe, 0x62, 0x3e, 0x97, 0xfe, 0xfa, 0x2e, 0xef, 0xac, 0x79, 0xb1, - 0x5f, 0x69, 0x34, 0x14, 0xc6, 0xcf, 0x65, 0x60, 0x26, 0xa5, 0x1d, 0xe4, 0x22, 0xe4, 0xf1, 0xe1, - 0x02, 0xe5, 0x82, 0x39, 0xf6, 0x60, 0x01, 0x96, 0x93, 0xcf, 0xc1, 0x48, 0x65, 0xad, 0x56, 0x2b, - 0xad, 0xc9, 0x23, 0x1b, 0x17, 0x57, 0x6d, 0xdf, 0xf2, 0x6d, 0xfd, 0x5e, 0x4a, 0xa0, 0x91, 0xd7, - 0x61, 0xb8, 0xba, 0x81, 0x04, 0xdc, 0xc3, 0x09, 0x8f, 0x30, 0x4e, 0x27, 0x8e, 0x2f, 0x90, 0x8c, - 0x1f, 0xcd, 0x00, 0x49, 0x0e, 0x2a, 0xb9, 0x06, 0x63, 0xea, 0xd4, 0xf1, 0x03, 0x26, 0xde, 0xa1, - 0x28, 0x13, 0x63, 0xaa, 0x38, 0xa4, 0x02, 0x43, 0xf8, 0xd0, 0x52, 0x78, 0x21, 0x96, 0xba, 0x01, - 0x9c, 0x49, 0x6c, 0x00, 0x43, 0xf8, 0x8c, 0x93, 0xc9, 0x89, 0x8d, 0xdf, 0xce, 0x00, 0x49, 0x6e, - 0x9a, 0x03, 0x5f, 0xc8, 0xbf, 0xa1, 0x44, 0xa8, 0xaa, 0xa9, 0xc9, 0xc3, 0x77, 0x25, 0xd4, 0xc3, - 0x52, 0x14, 0xcb, 0x7a, 0x51, 0x3b, 0x9c, 0xf7, 0x0e, 0x6b, 0xba, 0x0c, 0x43, 0xf7, 0xa9, 0xb7, - 0x2d, 0x9d, 0xf7, 0xd0, 0xe1, 0xe7, 0x01, 0x03, 0xa8, 0x87, 0x55, 0xc4, 0x30, 0xfe, 0x28, 0x03, - 0xb3, 0x69, 0x9a, 0xdc, 0x31, 0xd1, 0x47, 0x46, 0x2c, 0x70, 0x0a, 0x2f, 0xe3, 0xb9, 0x37, 0x50, - 0x18, 0x2e, 0xb5, 0x00, 0x43, 0xac, 0xb3, 0x72, 0x86, 0xd1, 0x58, 0xc0, 0x46, 0xc3, 0x37, 0x39, - 0x9c, 0x21, 0xf0, 0x8c, 0x4a, 0x79, 0x4c, 0x9c, 0x85, 0x08, 0xa8, 0x28, 0x98, 0x1c, 0xce, 0x10, - 0xee, 0xba, 0x8d, 0xf0, 0x8d, 0x51, 0x44, 0x68, 0x31, 0x80, 0xc9, 0xe1, 0xe4, 0x22, 0x8c, 0xac, - 0xb7, 0x57, 0xa9, 0xfd, 0x40, 0x3e, 0x8d, 0x81, 0xce, 0x03, 0x6e, 0xdb, 0x6a, 0x32, 0x98, 0x29, - 0x0b, 0x8d, 0x9f, 0xca, 0xc0, 0x74, 0x42, 0x89, 0x3c, 0x3e, 0xc0, 0xaa, 0x7f, 0xa4, 0xc3, 0x20, - 0xfd, 0xe3, 0xcd, 0xcf, 0xa7, 0x37, 0xdf, 0xf8, 0x3f, 0xf3, 0x70, 0xa6, 0xc7, 0x99, 0x3e, 0x8a, - 0xc4, 0xca, 0x1c, 0x1b, 0x89, 0xf5, 0x55, 0x76, 0x86, 0xb6, 0x9d, 0x96, 0xbf, 0xe9, 0x46, 0x2d, - 0x8e, 0x1c, 0xba, 0xb1, 0x4c, 0x3e, 0xb3, 0x2a, 0x3d, 0x7f, 0xcf, 0xf2, 0xa7, 0xae, 0xad, 0xc0, - 0x4d, 0xaa, 0x14, 0x1a, 0xb3, 0x44, 0x2c, 0x54, 0xee, 0xcf, 0x49, 0x2c, 0x94, 0xee, 0x9d, 0x9f, - 0x7f, 0xa2, 0xde, 0xf9, 0xe9, 0x9e, 0x7d, 0x43, 0x8f, 0xe3, 0xe7, 0x59, 0x86, 0x09, 0xee, 0x3d, - 0x51, 0xf2, 0xf9, 0x24, 0x0d, 0x27, 0x3c, 0x2e, 0x6c, 0x3f, 0x39, 0x17, 0x1a, 0x0d, 0x59, 0xd1, - 0x3d, 0xc9, 0x47, 0xf0, 0xd6, 0xe7, 0x62, 0x6f, 0x4f, 0x71, 0xed, 0xb6, 0x57, 0x25, 0x35, 0x7e, - 0x2a, 0xab, 0x07, 0x4a, 0xfd, 0x79, 0x5c, 0x79, 0x97, 0x61, 0x68, 0x6b, 0x8f, 0x7a, 0x52, 0xde, - 0x61, 0x43, 0x0e, 0x18, 0x40, 0x6d, 0x08, 0x62, 0x90, 0x9b, 0x30, 0xb9, 0xc1, 0x67, 0x42, 0x0e, - 0x6f, 0x3e, 0x3a, 0x6a, 0x75, 0x84, 0x41, 0x20, 0x65, 0x7c, 0x63, 0x54, 0xc6, 0x2d, 0x38, 0xa7, - 0x7d, 0x90, 0x22, 0xb1, 0x03, 0x77, 0xe8, 0xe6, 0x3b, 0xe2, 0x64, 0xe4, 0xc2, 0x1e, 0x49, 0x0f, - 0x33, 0x06, 0x35, 0x76, 0xe0, 0xf9, 0xbe, 0x8c, 0xd8, 0x46, 0x04, 0x9d, 0xf0, 0x57, 0xcc, 0xeb, - 0xac, 0x2f, 0xa9, 0xa9, 0xd0, 0x19, 0xdf, 0x0b, 0xe3, 0xea, 0x28, 0xa3, 0x4c, 0x65, 0xbf, 0x85, - 0x50, 0xe3, 0x32, 0x95, 0x01, 0x4c, 0x0e, 0x3f, 0xf6, 0x79, 0xfa, 0x68, 0xfa, 0x73, 0xc7, 0x4d, - 0x3f, 0xab, 0x1c, 0x3f, 0x59, 0xa5, 0x72, 0xfc, 0xad, 0x56, 0x8e, 0x99, 0x1b, 0x4c, 0x0e, 0x7f, - 0xa2, 0x95, 0xff, 0x96, 0x7c, 0x20, 0x00, 0xfd, 0xc5, 0xe5, 0x99, 0x38, 0x7a, 0xfe, 0x73, 0x26, - 0xed, 0xa4, 0x1b, 0x61, 0x46, 0x9b, 0x64, 0xf6, 0xb8, 0x4d, 0xf2, 0x24, 0x0b, 0xf1, 0x2a, 0x8c, - 0x94, 0xc4, 0x9d, 0x6c, 0x3e, 0x52, 0x6c, 0xec, 0xc4, 0x05, 0xac, 0xc4, 0x32, 0x7e, 0x36, 0x03, - 0xa7, 0x52, 0x4d, 0x65, 0xac, 0x56, 0x6e, 0x93, 0x53, 0xbe, 0xc3, 0xb8, 0x41, 0x8e, 0x63, 0x9c, - 0x24, 0x6c, 0x77, 0xf0, 0xbe, 0x18, 0x2f, 0xc0, 0x68, 0x78, 0x51, 0x43, 0x66, 0xe5, 0xd4, 0xa1, - 0xa3, 0x8e, 0xb4, 0xf7, 0xd7, 0x00, 0x58, 0x0b, 0x9e, 0xa8, 0x5b, 0x99, 0xf1, 0x5b, 0x59, 0xfe, - 0x78, 0xd4, 0x53, 0x9b, 0x49, 0x2f, 0xdd, 0x17, 0x8c, 0x75, 0xa9, 0x77, 0xfe, 0x3c, 0xb2, 0x0c, - 0xc3, 0xb5, 0xc0, 0x0e, 0xba, 0x32, 0xda, 0x78, 0x46, 0x25, 0xc3, 0x82, 0xfb, 0x8b, 0x51, 0xbc, - 0xa9, 0x8f, 0x10, 0xed, 0x70, 0x80, 0x10, 0xc5, 0xa5, 0xcc, 0x81, 0x71, 0x95, 0x96, 0x7c, 0x00, - 0x93, 0x32, 0x3d, 0x18, 0x0f, 0xc1, 0x16, 0x97, 0x4a, 0xd2, 0x39, 0x41, 0xa6, 0x07, 0x53, 0x43, - 0xb6, 0x35, 0x7c, 0x55, 0x52, 0x77, 0x54, 0x64, 0xe3, 0x8f, 0x87, 0xf9, 0x3a, 0x10, 0x79, 0xfe, - 0xb6, 0x61, 0x72, 0xbd, 0x5a, 0x29, 0x2b, 0x86, 0x2f, 0xfd, 0x49, 0x87, 0xe5, 0x87, 0x01, 0xf5, - 0xda, 0x76, 0x53, 0x20, 0x1c, 0x46, 0x7b, 0x83, 0xeb, 0x34, 0xea, 0xe9, 0x46, 0xb1, 0x18, 0x47, - 0x56, 0x07, 0x3f, 0xdc, 0x84, 0x75, 0x64, 0x07, 0xac, 0xc3, 0xb7, 0x5b, 0xcd, 0x1e, 0x75, 0xe8, - 0x1c, 0xc9, 0x1e, 0x14, 0x6f, 0xa1, 0x1e, 0xa3, 0xd4, 0x92, 0xeb, 0x5f, 0xcb, 0x05, 0x51, 0xcb, - 0xb3, 0x5c, 0x01, 0x4a, 0xaf, 0x27, 0xc1, 0x35, 0xfa, 0x80, 0xf3, 0xc7, 0x7e, 0xc0, 0x7f, 0x25, - 0x03, 0xc3, 0x5c, 0x51, 0x12, 0xeb, 0xab, 0x87, 0x2a, 0xb6, 0xf5, 0x64, 0x54, 0xb1, 0x22, 0x0a, - 0x70, 0x6d, 0xa5, 0xf1, 0x32, 0x52, 0x89, 0x2d, 0x58, 0xe9, 0xa2, 0x88, 0x26, 0x6c, 0x5e, 0x72, - 0xfc, 0x7a, 0x25, 0xd5, 0x28, 0x34, 0x77, 0xe4, 0xd8, 0xe8, 0x2f, 0x19, 0xce, 0x3c, 0x22, 0x42, - 0x73, 0xf5, 0x80, 0xdc, 0x55, 0x18, 0x15, 0x01, 0xbf, 0x4b, 0x87, 0xe2, 0xa2, 0xaa, 0xa8, 0x5d, - 0x83, 0x37, 0x96, 0x0e, 0x23, 0x25, 0x50, 0x84, 0x0c, 0x5b, 0xdb, 0x87, 0xda, 0x23, 0x59, 0x12, - 0x91, 0xac, 0xf3, 0xc7, 0x63, 0x78, 0x26, 0x44, 0x3d, 0x4d, 0x71, 0x08, 0x17, 0xa9, 0x44, 0x64, - 0xd4, 0x60, 0x4a, 0xe2, 0xc3, 0x88, 0x07, 0x59, 0x85, 0xa2, 0x78, 0x58, 0x9f, 0xfb, 0x51, 0x54, - 0x2b, 0x3c, 0xa8, 0x54, 0xb8, 0xbf, 0xc9, 0x67, 0xf9, 0x85, 0x07, 0x86, 0x1e, 0xcf, 0x91, 0xa0, - 0x64, 0x07, 0xb7, 0x62, 0x7c, 0xf5, 0x91, 0xb7, 0x61, 0x2c, 0xcc, 0x44, 0x19, 0x46, 0x94, 0xa1, - 0xc1, 0x3a, 0x4a, 0x5d, 0xa9, 0xc5, 0x96, 0xa9, 0xe8, 0x64, 0x11, 0x0a, 0xec, 0x23, 0x8e, 0x3f, - 0xcf, 0xd5, 0x15, 0x30, 0xd5, 0x4d, 0x5c, 0xe2, 0x91, 0x1a, 0xcc, 0xb0, 0x8f, 0xa6, 0xe6, 0xb4, - 0x77, 0x9b, 0x74, 0xd5, 0xdd, 0x75, 0xbb, 0xc1, 0x3d, 0x73, 0x55, 0x08, 0x57, 0xae, 0x2a, 0xdb, - 0xad, 0xa6, 0x56, 0xec, 0x69, 0x8f, 0xaf, 0xa6, 0x50, 0x2b, 0x32, 0xec, 0x0f, 0xb2, 0x30, 0xa6, - 0xac, 0x27, 0x72, 0x19, 0x0a, 0x55, 0x7f, 0xd5, 0xad, 0xef, 0x87, 0xb9, 0xa6, 0x26, 0x1e, 0x1d, - 0x2d, 0x8c, 0x3a, 0xbe, 0xd5, 0x44, 0xa0, 0x19, 0x16, 0x93, 0x25, 0x98, 0xe0, 0x7f, 0xc9, 0x6c, - 0xde, 0xd9, 0xc8, 0xdb, 0x8d, 0x23, 0xcb, 0x3c, 0xde, 0xaa, 0x5c, 0xd3, 0x48, 0xc8, 0xd7, 0x00, - 0x38, 0x00, 0xa3, 0x13, 0x73, 0x83, 0xc7, 0x55, 0x8a, 0x0a, 0x52, 0xe2, 0x12, 0x15, 0x86, 0xe4, - 0x1b, 0x3c, 0x73, 0xa5, 0x5c, 0xff, 0xf9, 0xc1, 0x03, 0x43, 0x19, 0x7f, 0x2b, 0x3d, 0x3e, 0x5d, - 0x65, 0x29, 0xd2, 0xe2, 0xcd, 0x9b, 0xb4, 0xee, 0x3e, 0xa0, 0xde, 0x61, 0x29, 0x40, 0x44, 0x05, - 0xc3, 0xf8, 0x1f, 0x33, 0xca, 0x57, 0x43, 0xd6, 0xf0, 0x45, 0x39, 0xbe, 0x22, 0x84, 0xcf, 0x46, - 0xa8, 0xcc, 0x4b, 0xb8, 0x49, 0x77, 0x96, 0x9e, 0x15, 0xce, 0x96, 0x33, 0xe1, 0xba, 0x8a, 0xbd, - 0x34, 0xc7, 0x81, 0xe4, 0x7d, 0xc8, 0xe3, 0xd0, 0x65, 0x8f, 0xed, 0x9a, 0xdc, 0x4f, 0xf3, 0x6c, - 0xcc, 0xb0, 0x23, 0x48, 0x49, 0x3e, 0x27, 0x22, 0xbb, 0xf8, 0xe0, 0x4f, 0x2a, 0x9b, 0x22, 0x6b, - 0x47, 0xb8, 0x91, 0x46, 0x29, 0x0a, 0x94, 0xd5, 0xf3, 0x37, 0xb2, 0x50, 0x8c, 0x7f, 0xab, 0xe4, - 0x3d, 0x18, 0x97, 0x3b, 0x1d, 0x3e, 0x39, 0xcc, 0x7a, 0x39, 0x2e, 0xd2, 0x4b, 0xcb, 0xed, 0x2e, - 0xfe, 0xe2, 0xb0, 0x4a, 0xc0, 0xb4, 0x8e, 0x4d, 0x91, 0x32, 0x48, 0xf9, 0x4a, 0x02, 0x37, 0xe8, - 0xc4, 0x92, 0x1f, 0x4a, 0x34, 0xf2, 0x06, 0xe4, 0xee, 0xde, 0x2c, 0x89, 0x30, 0x02, 0x29, 0x92, - 0xee, 0xde, 0x2c, 0xf1, 0xaf, 0x99, 0xbb, 0x49, 0xe9, 0x4e, 0x5b, 0x0c, 0x9f, 0xac, 0x2a, 0xb9, - 0x45, 0x87, 0xb5, 0xf7, 0x7f, 0x24, 0x38, 0xec, 0xdc, 0xf1, 0x49, 0x46, 0xf9, 0x5b, 0xc6, 0x22, - 0xcb, 0xde, 0xbf, 0x9d, 0x83, 0xd1, 0xb0, 0x7e, 0x42, 0x00, 0x95, 0x2a, 0x71, 0x92, 0xc1, 0xbf, - 0xc9, 0x59, 0x28, 0x48, 0x3d, 0x4a, 0x44, 0x13, 0x8c, 0xf8, 0x42, 0x87, 0x9a, 0x03, 0xa9, 0x30, - 0xf1, 0xcf, 0xdc, 0x94, 0x3f, 0xc9, 0x35, 0x08, 0xb5, 0xa1, 0x5e, 0x6a, 0x53, 0x9e, 0x4d, 0x98, - 0x19, 0xa2, 0x91, 0x49, 0xc8, 0x3a, 0x3c, 0x73, 0xcb, 0xa8, 0x99, 0x75, 0x1a, 0xe4, 0x3d, 0x28, - 0xd8, 0x8d, 0x06, 0x6d, 0x58, 0xb6, 0x74, 0x7e, 0xe8, 0xb7, 0x68, 0x0a, 0x8c, 0x1b, 0xdf, 0x04, - 0x90, 0xaa, 0x14, 0x90, 0x12, 0x8c, 0x36, 0x6d, 0xee, 0x48, 0xd5, 0x18, 0x60, 0x47, 0x89, 0x38, - 0x14, 0x18, 0xd9, 0x3d, 0x9f, 0x36, 0xc8, 0xcb, 0x90, 0x67, 0xb3, 0x29, 0xb6, 0x10, 0xa9, 0xbe, - 0xb1, 0xc9, 0xe4, 0x03, 0xb6, 0xf2, 0x8c, 0x89, 0x08, 0xe4, 0x45, 0xc8, 0x75, 0x17, 0x77, 0xc4, - 0xe6, 0x50, 0x8c, 0xf2, 0xfc, 0x86, 0x68, 0xac, 0x98, 0x5c, 0x87, 0xc2, 0x81, 0x9e, 0x22, 0xf6, - 0x54, 0x6c, 0x1a, 0x43, 0xfc, 0x10, 0x71, 0xa9, 0x00, 0xc3, 0x7c, 0x23, 0x30, 0x9e, 0x07, 0x88, - 0xaa, 0x4e, 0x06, 0x7d, 0x18, 0x5f, 0x83, 0xd1, 0xb0, 0x4a, 0x72, 0x0e, 0x60, 0x9f, 0x1e, 0x5a, - 0x7b, 0x76, 0xbb, 0xd1, 0xe4, 0xfa, 0xdd, 0xb8, 0x39, 0xba, 0x4f, 0x0f, 0x57, 0x10, 0x40, 0xce, - 0xc0, 0x48, 0x87, 0xcd, 0xaa, 0x58, 0xba, 0xe3, 0xe6, 0x70, 0xa7, 0xbb, 0xcd, 0x56, 0xe8, 0x1c, - 0x8c, 0xa0, 0xe5, 0x4d, 0x7c, 0x68, 0x13, 0xa6, 0xfc, 0x69, 0xfc, 0xe7, 0x59, 0x7c, 0xca, 0x40, - 0x69, 0x27, 0xb9, 0x00, 0x13, 0x75, 0x8f, 0xe2, 0x9e, 0x63, 0x33, 0x4d, 0x4a, 0xd4, 0x33, 0x1e, - 0x01, 0xab, 0x0d, 0x72, 0x11, 0xa6, 0xc4, 0xf3, 0xdd, 0xac, 0x41, 0xf5, 0x6d, 0x91, 0xcf, 0x79, - 0xdc, 0x9c, 0xe0, 0xe0, 0x3b, 0xf4, 0xb0, 0xbc, 0x8d, 0x19, 0x87, 0x8a, 0x6a, 0xc2, 0xc8, 0x20, - 0x7c, 0x75, 0xd1, 0x9c, 0x52, 0xe0, 0xe8, 0xd3, 0x74, 0x1a, 0x86, 0x6d, 0x7b, 0xb7, 0xeb, 0xf0, - 0xcc, 0x20, 0xe3, 0xa6, 0xf8, 0x45, 0x5e, 0x85, 0xe9, 0x28, 0xaf, 0xa8, 0xec, 0xc6, 0x10, 0x76, - 0xa3, 0x18, 0x16, 0x94, 0x39, 0x9c, 0xbc, 0x0e, 0x44, 0xad, 0xcf, 0xdd, 0xfe, 0x88, 0xd6, 0xf9, - 0x52, 0x1b, 0x37, 0xa7, 0x95, 0x92, 0x75, 0x2c, 0x20, 0x2f, 0xc0, 0xb8, 0x47, 0x7d, 0xd4, 0xe2, - 0x70, 0xd8, 0xf0, 0xa5, 0x1f, 0x73, 0x4c, 0xc2, 0xd8, 0xd8, 0x5d, 0x82, 0xa2, 0x32, 0x1c, 0x98, - 0x93, 0x93, 0x27, 0x33, 0x36, 0x27, 0x23, 0xb8, 0xd9, 0xa9, 0x36, 0x8c, 0x25, 0x98, 0x4e, 0x7c, - 0xb9, 0xca, 0x4b, 0xb9, 0x5c, 0x12, 0xf5, 0x7f, 0x29, 0xd7, 0x68, 0xc3, 0xb8, 0x2a, 0x89, 0x8f, - 0xc9, 0xa9, 0x7d, 0x1a, 0x23, 0xcb, 0xb9, 0x98, 0x1a, 0x7e, 0x74, 0xb4, 0x90, 0x75, 0x1a, 0x18, - 0x4f, 0x7e, 0x09, 0x0a, 0x52, 0x69, 0x10, 0x7b, 0x35, 0x5a, 0x4e, 0x85, 0xb6, 0x7a, 0x68, 0x86, - 0xa5, 0xc6, 0xcb, 0x30, 0x22, 0x84, 0x6d, 0x7f, 0x7b, 0xa9, 0xf1, 0xc3, 0x59, 0x98, 0x32, 0x29, - 0x13, 0x05, 0x94, 0x27, 0xd2, 0x7f, 0x6a, 0x8f, 0x6f, 0xe9, 0xf9, 0xc9, 0xb4, 0xbe, 0xf5, 0x49, - 0x61, 0xff, 0xf7, 0x33, 0x30, 0x93, 0x82, 0xfb, 0x89, 0x9e, 0x5b, 0xbb, 0x01, 0xa3, 0x15, 0xc7, - 0x6e, 0x96, 0x1a, 0x8d, 0x30, 0xcc, 0x1c, 0x55, 0xcd, 0x06, 0x5b, 0x69, 0x36, 0x83, 0xaa, 0xdb, - 0x6e, 0x88, 0x4a, 0x5e, 0x11, 0x8b, 0x22, 0x7a, 0xea, 0x1a, 0x17, 0xc5, 0xc7, 0x47, 0x0b, 0xc0, - 0xdb, 0x14, 0x3d, 0xe9, 0x89, 0x39, 0x03, 0x39, 0x30, 0x72, 0x03, 0x7f, 0x6a, 0xa7, 0x2e, 0x3d, - 0x67, 0x60, 0xbc, 0x7b, 0x03, 0x65, 0xb1, 0xff, 0xb1, 0x2c, 0x9c, 0x4e, 0x27, 0xfc, 0xa4, 0x2f, - 0xe7, 0xe1, 0xfb, 0x01, 0x4a, 0x9e, 0x53, 0x7c, 0x39, 0x8f, 0x3f, 0x36, 0x80, 0xf8, 0x11, 0x02, - 0xd9, 0x81, 0x89, 0x55, 0xdb, 0x0f, 0x56, 0xa8, 0xed, 0x05, 0xdb, 0xd4, 0x0e, 0x06, 0xd0, 0x3d, - 0x5f, 0x94, 0xd7, 0x92, 0xb8, 0xfd, 0xed, 0x49, 0xca, 0x98, 0x76, 0xa8, 0xb3, 0x0d, 0x17, 0x4a, - 0x7e, 0x80, 0x85, 0xf2, 0x4d, 0x98, 0xaa, 0xd1, 0x96, 0xdd, 0xd9, 0x73, 0x3d, 0x19, 0x47, 0x78, - 0x05, 0x26, 0x42, 0x50, 0xea, 0x6a, 0xd1, 0x8b, 0x35, 0x7c, 0x65, 0x20, 0x22, 0x51, 0xa2, 0x17, - 0x1b, 0x7f, 0x33, 0x0b, 0x67, 0x4a, 0x75, 0xe1, 0x2d, 0x24, 0x0a, 0xa4, 0x53, 0xe3, 0xa7, 0x5c, - 0x37, 0xb9, 0x0a, 0xa3, 0x77, 0xed, 0x87, 0xab, 0xd4, 0xf6, 0xa9, 0x2f, 0xde, 0x2d, 0xe2, 0x8a, - 0x9a, 0xfd, 0x30, 0x72, 0xa2, 0x31, 0x23, 0x1c, 0xf5, 0x24, 0x9b, 0x7f, 0xcc, 0x93, 0xac, 0x01, - 0xc3, 0x2b, 0x6e, 0xb3, 0x21, 0xb6, 0x31, 0x71, 0xbd, 0xb6, 0x87, 0x10, 0x53, 0x94, 0xb0, 0x03, - 0xe0, 0x64, 0xd8, 0x62, 0x6c, 0xc2, 0xa7, 0x3e, 0x24, 0x17, 0x61, 0x04, 0x2b, 0xaa, 0x56, 0xd4, - 0x4d, 0xa3, 0x49, 0xf1, 0xf5, 0x99, 0x86, 0x29, 0x0b, 0xd5, 0x91, 0x18, 0x7a, 0xbc, 0x91, 0x30, - 0xfe, 0x1d, 0xbc, 0xb9, 0x53, 0x7b, 0xc9, 0x76, 0x22, 0xa5, 0x21, 0x99, 0x01, 0x1b, 0x92, 0x7d, - 0x62, 0x53, 0x92, 0xeb, 0x39, 0x25, 0xdf, 0xc9, 0xc2, 0x58, 0xd8, 0xd8, 0xcf, 0x58, 0xb2, 0xdd, - 0xb0, 0x5f, 0x03, 0xc5, 0xfe, 0xd7, 0x14, 0x59, 0x21, 0x42, 0xec, 0xdf, 0x87, 0x61, 0xf1, 0x31, - 0x65, 0x62, 0xce, 0x7d, 0xb1, 0xd9, 0x5d, 0x9a, 0x14, 0xac, 0x87, 0x71, 0x42, 0x7d, 0x53, 0xd0, - 0x61, 0x72, 0x85, 0x2d, 0xba, 0x2d, 0x2e, 0x72, 0x9f, 0xda, 0x3d, 0x2a, 0x3d, 0xb9, 0x42, 0xd4, - 0xb1, 0x81, 0x76, 0xa7, 0x7f, 0x5c, 0x80, 0x62, 0x9c, 0xe4, 0xf8, 0x74, 0xc6, 0x1b, 0xdd, 0x6d, - 0xae, 0x85, 0xf3, 0x74, 0xc6, 0x9d, 0xee, 0xb6, 0xc9, 0x60, 0xe8, 0xe7, 0xe1, 0x39, 0x0f, 0xb0, - 0xd7, 0xe3, 0xc2, 0xcf, 0xc3, 0x73, 0x1e, 0x68, 0x7e, 0x1e, 0x9e, 0xf3, 0x00, 0x8f, 0xbe, 0xab, - 0x35, 0x8c, 0x07, 0x45, 0x15, 0x5c, 0x1c, 0x7d, 0x9b, 0x7e, 0xfc, 0x89, 0x11, 0x89, 0xc6, 0xb6, - 0xca, 0x25, 0x6a, 0x7b, 0x22, 0xf5, 0xae, 0x10, 0x67, 0xb8, 0x55, 0x6e, 0x23, 0x98, 0xbf, 0xde, - 0x6b, 0xaa, 0x48, 0xa4, 0x09, 0x44, 0xf9, 0x29, 0x3f, 0xe0, 0xe3, 0x4f, 0x83, 0xd2, 0x31, 0x67, - 0x56, 0x65, 0x6d, 0xa9, 0x5f, 0x73, 0x0a, 0xdf, 0x27, 0x69, 0x80, 0xdc, 0x10, 0xf9, 0xc4, 0xd0, - 0xe4, 0x51, 0x38, 0x96, 0x99, 0x0c, 0x98, 0x06, 0x9e, 0x6f, 0x2c, 0x34, 0x7c, 0x44, 0x4c, 0xc8, - 0xbb, 0x30, 0xa6, 0x46, 0xf9, 0xf2, 0x58, 0xd4, 0xe7, 0x78, 0x8a, 0xa8, 0x1e, 0x0f, 0xc8, 0xa9, - 0x04, 0x64, 0x1b, 0xce, 0x94, 0xdd, 0xb6, 0xdf, 0x6d, 0xc9, 0x64, 0x54, 0x51, 0x0a, 0x4c, 0x08, - 0x1f, 0x80, 0x7f, 0xb1, 0x2e, 0x50, 0x44, 0x50, 0xa9, 0xf4, 0x9c, 0xd6, 0x0f, 0x20, 0xbd, 0x18, - 0x91, 0x4d, 0x18, 0x43, 0x23, 0x9e, 0x70, 0xcd, 0x1a, 0xd3, 0xc5, 0x46, 0x54, 0x52, 0x61, 0x1f, - 0x06, 0xcf, 0xa6, 0x62, 0xb7, 0x9a, 0xd2, 0x71, 0x57, 0x35, 0x46, 0x2a, 0xc8, 0xe4, 0x6b, 0x30, - 0xc9, 0x8f, 0x9b, 0x5b, 0x74, 0x9b, 0xaf, 0x9d, 0x71, 0xed, 0xec, 0xac, 0x17, 0xf2, 0x8b, 0x5e, - 0x61, 0x3a, 0x3d, 0xa0, 0xdb, 0x7c, 0xee, 0x35, 0xb7, 0x79, 0x0d, 0x9f, 0xdc, 0x83, 0x99, 0x15, - 0xdb, 0xe7, 0x40, 0x25, 0x5c, 0x73, 0x02, 0x6d, 0x8a, 0xe8, 0xce, 0xb8, 0x67, 0xfb, 0xd2, 0x16, - 0x9b, 0x1a, 0x9e, 0x99, 0x46, 0x4f, 0x7e, 0x38, 0x03, 0x73, 0x9a, 0xa9, 0x56, 0x38, 0xd5, 0xb4, - 0x68, 0x3b, 0x40, 0xff, 0xf8, 0xc9, 0xf0, 0xdd, 0xe0, 0x5e, 0x68, 0x7c, 0x4a, 0x62, 0xd6, 0x60, - 0x2f, 0x2a, 0x57, 0xfd, 0x04, 0x7b, 0xf1, 0x10, 0x1f, 0x2a, 0x7e, 0xd3, 0x53, 0xfa, 0x87, 0x1a, - 0xfb, 0xae, 0x25, 0x9a, 0x71, 0x23, 0x3e, 0xde, 0xc2, 0x34, 0x93, 0x09, 0x4d, 0x33, 0xb3, 0x30, - 0x84, 0xa3, 0x2a, 0xb3, 0x4b, 0xe0, 0x0f, 0xe3, 0x73, 0xaa, 0x1c, 0x12, 0x6a, 0x61, 0x5f, 0x39, - 0x64, 0xfc, 0xb7, 0xc3, 0x30, 0x15, 0x5b, 0x16, 0xe2, 0x9c, 0x9a, 0x49, 0x9c, 0x53, 0x6b, 0x00, - 0xdc, 0x38, 0x39, 0xa0, 0x15, 0x51, 0xc6, 0xe6, 0x8c, 0x89, 0xd8, 0xb6, 0xf0, 0x9b, 0x52, 0xd8, - 0x30, 0xa6, 0xfc, 0x8b, 0x1d, 0xd0, 0xaa, 0x1b, 0x32, 0xe5, 0x1f, 0xbd, 0xc2, 0x34, 0x62, 0x43, - 0x16, 0x60, 0x08, 0x53, 0xc2, 0xa9, 0xa1, 0x51, 0x0e, 0x03, 0x98, 0x1c, 0x4e, 0x2e, 0xc0, 0x30, - 0x53, 0xa2, 0xaa, 0x15, 0x21, 0x04, 0x71, 0x6f, 0x61, 0x5a, 0x16, 0xd3, 0x58, 0x44, 0x11, 0xb9, - 0x01, 0xe3, 0xfc, 0x2f, 0x91, 0x15, 0x60, 0x58, 0xf7, 0xf4, 0xb2, 0x9c, 0x86, 0x4c, 0x0c, 0xa0, - 0xe1, 0xb1, 0xd3, 0x45, 0xad, 0xbb, 0xcd, 0xdf, 0xbd, 0x17, 0x39, 0x44, 0xf1, 0x74, 0xe1, 0x73, - 0x20, 0xbe, 0xcb, 0x1d, 0x22, 0x30, 0x5d, 0x46, 0x38, 0x28, 0x17, 0xf0, 0x4c, 0x89, 0xba, 0x0c, - 0x77, 0x4c, 0x36, 0x45, 0x09, 0xb9, 0xcc, 0x2f, 0x03, 0x50, 0x2d, 0xe4, 0x4f, 0x2a, 0xa1, 0xa5, - 0x1d, 0x0d, 0x13, 0xa8, 0x1b, 0x86, 0xc5, 0xac, 0x72, 0xf6, 0xf7, 0x72, 0xcb, 0x76, 0x9a, 0x42, - 0xac, 0x60, 0xe5, 0x88, 0x4b, 0x19, 0xd4, 0x8c, 0x10, 0xc8, 0xdb, 0x30, 0xc9, 0x7e, 0x94, 0xdd, - 0x56, 0xcb, 0x6d, 0x23, 0xfb, 0xb1, 0x28, 0xc1, 0x0c, 0x92, 0xd4, 0xb1, 0x88, 0xd7, 0x12, 0xc3, - 0x65, 0xfb, 0x09, 0x5e, 0x34, 0x76, 0xf9, 0x35, 0xc5, 0x78, 0xb4, 0x9f, 0x20, 0xa9, 0xcf, 0xe1, - 0xa6, 0x8a, 0x44, 0xde, 0x84, 0x09, 0xf6, 0xf3, 0x96, 0xf3, 0x80, 0xf2, 0x0a, 0x27, 0xa2, 0xab, - 0x6f, 0xa4, 0xda, 0x65, 0x25, 0xbc, 0x3e, 0x1d, 0x93, 0x7c, 0x19, 0x4e, 0x21, 0xa7, 0xba, 0xdb, - 0xa1, 0x8d, 0xd2, 0xce, 0x8e, 0xd3, 0x74, 0xb8, 0xeb, 0x0d, 0x8f, 0x7f, 0x47, 0xab, 0x31, 0xaf, - 0x18, 0x31, 0x2c, 0x3b, 0x42, 0x31, 0xd3, 0x29, 0xc9, 0x16, 0x14, 0xcb, 0x5d, 0x3f, 0x70, 0x5b, - 0xa5, 0x20, 0xf0, 0x9c, 0xed, 0x6e, 0x40, 0xfd, 0xb9, 0x29, 0x2d, 0x4a, 0x9c, 0x7d, 0x1c, 0x61, - 0x21, 0xb7, 0x07, 0xd5, 0x91, 0xc2, 0xb2, 0x43, 0x12, 0x33, 0xc1, 0xc4, 0xf8, 0x6f, 0x32, 0x30, - 0xa1, 0x91, 0x92, 0x37, 0x60, 0xfc, 0xa6, 0xe7, 0xd0, 0x76, 0xa3, 0x79, 0xa8, 0x1c, 0x54, 0xf1, - 0x14, 0xb3, 0x23, 0xe0, 0xbc, 0xd7, 0x1a, 0x5a, 0x68, 0xe7, 0xc9, 0xa6, 0xfa, 0xc5, 0x5d, 0xe5, - 0x11, 0x7a, 0x62, 0x81, 0xe6, 0xa2, 0xb4, 0x15, 0xb8, 0x40, 0xc5, 0xea, 0x54, 0x50, 0xc8, 0x3b, - 0x30, 0xcc, 0xaf, 0x24, 0x85, 0x93, 0xd6, 0xd9, 0xb4, 0x6e, 0xf2, 0x68, 0x50, 0x5c, 0x88, 0xe8, - 0x10, 0xe2, 0x9b, 0x82, 0xc8, 0xf8, 0xe9, 0x0c, 0x90, 0x24, 0xea, 0x31, 0x76, 0xaf, 0x63, 0x1d, - 0x4d, 0xde, 0x0f, 0xbf, 0xc6, 0x9c, 0x66, 0xe5, 0x65, 0x35, 0xf1, 0x02, 0x3e, 0xf0, 0xe2, 0xab, - 0x53, 0x0d, 0x71, 0xbc, 0xd8, 0xf8, 0xcb, 0x59, 0x80, 0x08, 0x9b, 0x7c, 0x91, 0xbf, 0xbc, 0xf1, - 0xe5, 0xae, 0xdd, 0x74, 0x76, 0x1c, 0x3d, 0x15, 0x1d, 0x32, 0xf9, 0xa6, 0x2c, 0x31, 0x75, 0x44, - 0xf2, 0x1e, 0x4c, 0xd5, 0x36, 0x74, 0x5a, 0xe5, 0x95, 0x01, 0xbf, 0x63, 0xc5, 0xc8, 0xe3, 0xd8, - 0xe8, 0x8c, 0xa9, 0xce, 0x06, 0x77, 0xc6, 0xe4, 0x13, 0x21, 0x4a, 0x98, 0x60, 0xa9, 0x6d, 0x08, - 0xff, 0xdf, 0x46, 0xb5, 0x22, 0xa4, 0x14, 0xb6, 0xce, 0xef, 0x58, 0x1d, 0xe1, 0x18, 0xcc, 0xe4, - 0x84, 0x86, 0x17, 0x0d, 0xe4, 0x50, 0x8f, 0x88, 0xcf, 0x9f, 0x41, 0xb3, 0x5f, 0xcb, 0x0d, 0xa8, - 0xb0, 0x76, 0x3c, 0xb5, 0xe7, 0x9e, 0xe8, 0x3e, 0x7b, 0x48, 0x0b, 0x64, 0xd3, 0x7a, 0x27, 0xbc, - 0x29, 0xae, 0x47, 0x87, 0x14, 0x7e, 0xb3, 0x9d, 0xe2, 0x7f, 0xf1, 0x77, 0x33, 0x70, 0x2a, 0x95, - 0x96, 0x5c, 0x01, 0x88, 0x6c, 0x4a, 0x62, 0x94, 0x50, 0x62, 0x46, 0xc9, 0x1a, 0x4c, 0x05, 0x83, - 0x7c, 0x35, 0x6e, 0x0d, 0x3a, 0x7e, 0x23, 0x9c, 0x97, 0xc9, 0x78, 0x74, 0x6b, 0x50, 0x8a, 0x0d, - 0xc8, 0xf8, 0xfb, 0x39, 0x98, 0x56, 0x72, 0x41, 0xf0, 0xb6, 0x1e, 0xe3, 0x1c, 0xbb, 0x0f, 0xe3, - 0xac, 0x37, 0x4e, 0x5d, 0x44, 0xd3, 0x70, 0xdf, 0x8b, 0x57, 0x12, 0xa1, 0x48, 0x82, 0xdb, 0x15, - 0x15, 0x99, 0xa7, 0xc8, 0x42, 0xd1, 0x89, 0xb6, 0xf6, 0x7a, 0x32, 0xaa, 0x46, 0x63, 0x4e, 0x7c, - 0x98, 0xa8, 0x1c, 0xb6, 0xed, 0x56, 0x58, 0x1b, 0xf7, 0xc1, 0x78, 0xb5, 0x67, 0x6d, 0x1a, 0x36, - 0xaf, 0x2e, 0x72, 0xda, 0xe7, 0x65, 0x29, 0xf1, 0xa2, 0x1a, 0xd5, 0xfc, 0x7b, 0x30, 0x9d, 0x68, - 0xf4, 0x89, 0xb2, 0x75, 0x6d, 0x01, 0x49, 0xb6, 0x23, 0x85, 0xc3, 0xab, 0x7a, 0x2e, 0xb8, 0x53, - 0xe1, 0x75, 0x2b, 0xbe, 0x07, 0xcc, 0x3d, 0x3a, 0x16, 0xd5, 0x5c, 0x5e, 0x3f, 0x93, 0x55, 0xc3, - 0xc1, 0x9e, 0xf6, 0xaf, 0xee, 0x7d, 0xed, 0x34, 0xfc, 0x7c, 0xaf, 0x39, 0x1d, 0xc8, 0xea, 0xf0, - 0xdd, 0x1c, 0x9c, 0xe9, 0x41, 0x49, 0x0e, 0xe3, 0x8b, 0x88, 0x5b, 0x21, 0xae, 0xf5, 0xaf, 0xf0, - 0x49, 0x2c, 0x25, 0xf2, 0x45, 0x1e, 0x10, 0x5e, 0xc7, 0x77, 0x6c, 0xc5, 0xf9, 0x9b, 0x3f, 0x81, - 0x1e, 0x42, 0xe3, 0x91, 0xe0, 0x1c, 0x4a, 0xde, 0x83, 0x21, 0x8c, 0x05, 0x8c, 0xe5, 0xa2, 0x62, - 0x18, 0x08, 0x57, 0x12, 0x77, 0xb1, 0x9f, 0x5a, 0xe2, 0x2e, 0x06, 0x20, 0x5f, 0x80, 0x5c, 0x69, - 0xab, 0x26, 0xe6, 0x65, 0x52, 0x25, 0xdf, 0xaa, 0x45, 0xf9, 0xc2, 0x6d, 0x2d, 0xb1, 0x37, 0xa3, - 0x60, 0x84, 0xb7, 0xca, 0x1b, 0x62, 0x56, 0x54, 0xc2, 0x5b, 0xe5, 0x8d, 0x88, 0x70, 0xb7, 0xae, - 0xe5, 0xf6, 0xb8, 0x55, 0xde, 0xf8, 0xf4, 0x96, 0xfd, 0x5f, 0xcb, 0xf2, 0x28, 0x76, 0xde, 0xb1, - 0xf7, 0x60, 0x5c, 0xcb, 0xd5, 0x99, 0x89, 0xf4, 0xb1, 0x30, 0x25, 0x6a, 0xcc, 0x69, 0x45, 0x23, - 0x90, 0x99, 0xf7, 0xd9, 0x6f, 0xd4, 0x78, 0x55, 0xf7, 0x90, 0x90, 0x03, 0xea, 0xc4, 0xf1, 0xcc, - 0xfb, 0x21, 0x09, 0xb9, 0x0e, 0x85, 0x4d, 0xda, 0xb6, 0xdb, 0x41, 0x68, 0x10, 0x45, 0xc7, 0xd3, - 0x00, 0x61, 0xba, 0xd6, 0x10, 0x22, 0xa2, 0x93, 0x64, 0x77, 0xdb, 0xaf, 0x7b, 0x0e, 0x66, 0xbb, - 0x08, 0xf7, 0x62, 0xee, 0x24, 0xa9, 0x94, 0xe8, 0x0c, 0x62, 0x44, 0xc6, 0xcf, 0x64, 0x60, 0x44, - 0x4c, 0x24, 0x7f, 0x31, 0x65, 0x37, 0xda, 0x4b, 0xc4, 0x8b, 0x29, 0xbb, 0x4e, 0xfc, 0xc5, 0x94, - 0x5d, 0x9e, 0x52, 0x62, 0x54, 0x04, 0x64, 0x86, 0x57, 0x83, 0xfc, 0xf1, 0x6e, 0x0e, 0xd4, 0xab, - 0x8d, 0x50, 0x07, 0x8d, 0x3e, 0x31, 0xfe, 0xb6, 0x68, 0xd9, 0xad, 0xf2, 0x06, 0x59, 0x84, 0xc2, - 0xaa, 0x5b, 0xb7, 0x95, 0x7d, 0x0e, 0xc5, 0x4e, 0x53, 0xc0, 0xd4, 0x01, 0x92, 0x78, 0xac, 0x7d, - 0x1b, 0x9e, 0x2b, 0xce, 0x32, 0x4a, 0xfb, 0x3a, 0x1c, 0x18, 0x6b, 0x5f, 0x88, 0x3a, 0x70, 0xfb, - 0x68, 0x8a, 0x90, 0xb8, 0x7f, 0x1d, 0x53, 0x92, 0xdf, 0x56, 0xa3, 0x7a, 0x44, 0x91, 0x94, 0x14, - 0xf3, 0xbd, 0x24, 0xc5, 0xfd, 0xeb, 0x66, 0x0a, 0x15, 0xde, 0xab, 0x45, 0xe0, 0x1a, 0xf5, 0x1e, - 0x3c, 0xc5, 0x52, 0x3a, 0xfd, 0x5e, 0x2d, 0xde, 0xbd, 0x81, 0x84, 0xf4, 0x7f, 0x95, 0x85, 0xd3, - 0xe9, 0x84, 0x6a, 0x5f, 0x32, 0x7d, 0xfa, 0x72, 0x09, 0x0a, 0x2b, 0xae, 0x1f, 0x28, 0x7e, 0x6a, - 0x68, 0xfe, 0xdf, 0x13, 0x30, 0x33, 0x2c, 0x65, 0x67, 0x6e, 0xf6, 0x77, 0xf8, 0x79, 0x22, 0x3f, - 0x8c, 0xdd, 0x66, 0x67, 0x6e, 0x5e, 0x44, 0x6e, 0x41, 0xc1, 0x14, 0x51, 0x25, 0xb1, 0xa1, 0x91, - 0xe0, 0x50, 0x9b, 0x22, 0x9e, 0x80, 0x68, 0x29, 0x53, 0x05, 0x8c, 0x94, 0x60, 0x44, 0xcc, 0x7e, - 0xec, 0xea, 0x38, 0x65, 0xc9, 0xe8, 0x59, 0x8c, 0x25, 0x1d, 0x93, 0x28, 0x78, 0x09, 0x58, 0xad, - 0xc8, 0x00, 0x11, 0x94, 0x28, 0xfc, 0x92, 0x50, 0x77, 0x09, 0x0c, 0x11, 0x8d, 0x1f, 0xce, 0x02, - 0x48, 0xab, 0xcd, 0x53, 0xbb, 0xc2, 0xbe, 0xa0, 0xad, 0x30, 0xc5, 0x43, 0x66, 0xf0, 0x17, 0xfe, - 0xd6, 0xd1, 0x53, 0x65, 0xf0, 0xf7, 0xfd, 0x16, 0x60, 0x68, 0x33, 0x32, 0x68, 0x89, 0x70, 0x05, - 0x34, 0x47, 0x73, 0xb8, 0xb1, 0x0d, 0xb3, 0xb7, 0x68, 0x10, 0x99, 0xb7, 0xe4, 0xd5, 0x63, 0x7f, - 0xb6, 0xaf, 0xc1, 0xa8, 0xc0, 0x0f, 0xe5, 0x17, 0xb7, 0xc5, 0x88, 0x74, 0x08, 0x68, 0x8b, 0x91, - 0x08, 0x4c, 0x1a, 0x55, 0x68, 0x93, 0x06, 0xf4, 0xd3, 0xad, 0xa6, 0x06, 0x84, 0x77, 0x05, 0x7b, - 0x36, 0x58, 0x0d, 0xc7, 0x8e, 0xcf, 0x7d, 0x38, 0x15, 0xb6, 0xfd, 0x49, 0xf2, 0xbd, 0xca, 0x8e, - 0x94, 0x22, 0x01, 0x70, 0xc4, 0xb1, 0x8f, 0xef, 0xc9, 0x43, 0x98, 0x97, 0x04, 0x5b, 0x4e, 0xe8, - 0xea, 0x37, 0x10, 0x2d, 0x79, 0x1b, 0xc6, 0x14, 0x1a, 0x91, 0xc0, 0x16, 0xcd, 0xd4, 0x07, 0x4e, - 0xb0, 0x67, 0xf9, 0x1c, 0xae, 0x9a, 0xa9, 0x15, 0x74, 0xe3, 0x43, 0x78, 0x36, 0x0c, 0x29, 0x49, - 0xa9, 0x3a, 0xc6, 0x3c, 0x73, 0x32, 0xe6, 0x6b, 0x51, 0xb7, 0xaa, 0xed, 0x30, 0x0c, 0x54, 0xf2, - 0x26, 0x6a, 0xb7, 0x44, 0x67, 0x9e, 0x4b, 0x04, 0x96, 0x2a, 0xf1, 0xa3, 0xc6, 0x5b, 0x4a, 0x63, - 0x53, 0x18, 0x6a, 0xc4, 0x99, 0x38, 0xf1, 0x0f, 0x67, 0x61, 0x6a, 0xbd, 0x5a, 0x29, 0x87, 0xde, - 0x47, 0x9f, 0xb1, 0xf7, 0x07, 0xb5, 0xbe, 0xf5, 0x96, 0x37, 0xc6, 0x3d, 0x98, 0x89, 0x0d, 0x03, - 0xaa, 0x0e, 0xef, 0xf2, 0x98, 0x87, 0x10, 0x2c, 0xd5, 0x86, 0xd3, 0x69, 0xec, 0xef, 0x5f, 0x37, - 0x63, 0xd8, 0xc6, 0x3f, 0x19, 0x8d, 0xf1, 0x15, 0x22, 0xec, 0x35, 0x18, 0xad, 0xfa, 0x7e, 0x97, - 0x7a, 0xf7, 0xcc, 0x55, 0xd5, 0x54, 0xe0, 0x20, 0xd0, 0xea, 0x7a, 0x4d, 0x33, 0x42, 0x20, 0x97, - 0xa1, 0x20, 0x72, 0xba, 0x4a, 0x99, 0x80, 0x56, 0xdb, 0x30, 0x25, 0xac, 0x19, 0x16, 0x93, 0x37, - 0x60, 0x9c, 0xff, 0xcd, 0x57, 0x9b, 0x18, 0x70, 0x34, 0x0e, 0x0a, 0x74, 0xbe, 0x3a, 0x4d, 0x0d, - 0x8d, 0xbc, 0x02, 0xb9, 0x52, 0xd9, 0x14, 0xe6, 0x20, 0xa1, 0x37, 0xe2, 0xab, 0xc2, 0x5d, 0xaa, - 0x1f, 0x22, 0xca, 0x26, 0xd3, 0xfe, 0x64, 0xc8, 0xb9, 0xb0, 0x64, 0xf3, 0xc7, 0x8f, 0x05, 0x2c, - 0xb6, 0x99, 0x21, 0x8c, 0x5c, 0x85, 0x91, 0x8a, 0xe3, 0x77, 0x9a, 0xf6, 0xa1, 0xb0, 0x63, 0xf3, - 0xc7, 0x75, 0x38, 0x48, 0x8b, 0x24, 0xe7, 0x20, 0x72, 0x59, 0x3e, 0x3a, 0x52, 0x88, 0x42, 0x27, - 0x7a, 0xbc, 0x2c, 0xf2, 0x1a, 0x0c, 0x8b, 0xcc, 0xa7, 0xa3, 0x4a, 0x4e, 0xf3, 0x78, 0xc6, 0x53, - 0x81, 0x93, 0x0c, 0x6e, 0x84, 0x27, 0x19, 0xdc, 0xb8, 0x0d, 0x67, 0x6e, 0xa1, 0xf5, 0x46, 0xcf, - 0x91, 0x72, 0xcf, 0xac, 0x0a, 0x7b, 0x38, 0x5e, 0x03, 0x71, 0x03, 0x4f, 0x3c, 0xcd, 0x8a, 0xd5, - 0xf5, 0xd4, 0xb7, 0xe2, 0x7a, 0x31, 0x22, 0x5f, 0x81, 0xd9, 0xb4, 0x22, 0x61, 0x35, 0xc7, 0x6c, - 0x20, 0xe9, 0x15, 0xa8, 0xd9, 0x40, 0xd2, 0x38, 0x90, 0x55, 0x28, 0x72, 0x78, 0xa9, 0xd1, 0x72, - 0xda, 0xdc, 0xf2, 0xcf, 0xad, 0xea, 0x18, 0xcb, 0x20, 0xb8, 0xda, 0xac, 0x90, 0xdf, 0x00, 0x68, - 0xd1, 0x2f, 0x31, 0x4a, 0xf2, 0x13, 0x19, 0x76, 0x9a, 0xe3, 0x79, 0x42, 0xef, 0x99, 0xab, 0xbe, - 0xc8, 0x24, 0x75, 0x3a, 0x0a, 0x6c, 0xa9, 0x05, 0x9e, 0xd3, 0xde, 0x15, 0x91, 0x2d, 0x9b, 0x22, - 0xb2, 0xe5, 0xed, 0x4f, 0x14, 0xd9, 0xc2, 0x59, 0xf9, 0x8f, 0x8e, 0x16, 0xc6, 0x3d, 0x51, 0x27, - 0x7e, 0x45, 0x5a, 0x0b, 0xf0, 0x9d, 0xf3, 0x66, 0xd3, 0x3d, 0xb8, 0xd7, 0x7e, 0x40, 0x3d, 0x67, - 0xc7, 0xa1, 0x0d, 0xde, 0xc9, 0x29, 0x94, 0xe0, 0xfc, 0x9d, 0x73, 0x56, 0x6e, 0x75, 0x43, 0x84, - 0x44, 0x47, 0x53, 0x39, 0xb0, 0x83, 0xa7, 0x8c, 0x9e, 0xe0, 0x91, 0x9a, 0xc5, 0xe8, 0xe0, 0x29, - 0x43, 0x2d, 0x2c, 0x5c, 0x46, 0xea, 0xe2, 0xd1, 0x48, 0xc8, 0x55, 0x18, 0xbe, 0x6b, 0x3f, 0x2c, - 0xed, 0x52, 0xf1, 0x98, 0xd4, 0x84, 0x14, 0x7f, 0x08, 0x5c, 0x2a, 0xfc, 0x1e, 0xf7, 0xce, 0x7f, - 0xc6, 0x14, 0x68, 0xe4, 0xdb, 0x19, 0x38, 0xcd, 0x3f, 0x63, 0xd9, 0xcb, 0x1a, 0x0d, 0x02, 0x36, - 0x0e, 0x22, 0xa5, 0x94, 0x7c, 0x8a, 0xa1, 0x56, 0x5b, 0x4f, 0xc7, 0xe3, 0xaf, 0x72, 0x0b, 0xc9, - 0x10, 0x0e, 0x9c, 0x2f, 0x4a, 0xb5, 0xbc, 0x91, 0xa9, 0xf4, 0xc2, 0xf3, 0xfc, 0x0b, 0xb2, 0xe5, - 0xe4, 0x75, 0x35, 0xa0, 0x30, 0x87, 0x7a, 0xee, 0x48, 0xcb, 0x7e, 0x68, 0xd9, 0xbb, 0x54, 0xbb, - 0xcf, 0x0e, 0x23, 0x0d, 0xcf, 0xf6, 0x6c, 0x1b, 0xb9, 0x01, 0x67, 0xe4, 0xfb, 0xec, 0x7b, 0x41, - 0xd0, 0xf1, 0x2d, 0x79, 0x16, 0x10, 0x11, 0x88, 0xe6, 0x29, 0x51, 0xbc, 0xc2, 0x4a, 0xe5, 0xf1, - 0xc0, 0x37, 0xfe, 0xc6, 0x28, 0xdf, 0xd3, 0x4a, 0xdd, 0x60, 0x4f, 0xee, 0x82, 0x8b, 0x69, 0x31, - 0x34, 0xdc, 0xb9, 0x4f, 0x89, 0xa1, 0xd1, 0x23, 0x67, 0xe4, 0x65, 0x44, 0x36, 0xf5, 0x32, 0xe2, - 0x35, 0x18, 0x2d, 0xef, 0xd1, 0xfa, 0x7e, 0x18, 0xc7, 0x50, 0x10, 0xd6, 0x5e, 0x06, 0xe4, 0x29, - 0x45, 0x23, 0x04, 0x72, 0x15, 0x00, 0x83, 0xea, 0xb8, 0x8a, 0xa4, 0xa4, 0x05, 0xc7, 0x18, 0x3c, - 0xe1, 0x2f, 0xa1, 0xa0, 0x20, 0xfb, 0x9a, 0x79, 0x53, 0x75, 0xb0, 0xe0, 0xec, 0x7d, 0x6f, 0x47, - 0xa0, 0x47, 0x08, 0xac, 0x7b, 0xca, 0x42, 0x17, 0x62, 0xb9, 0x98, 0xf8, 0x1a, 0x54, 0x24, 0xf4, - 0x5d, 0x94, 0x4e, 0xdb, 0x28, 0x95, 0xc7, 0x85, 0xef, 0x62, 0xe8, 0xe0, 0x6d, 0x46, 0x08, 0xe4, - 0x0b, 0x30, 0x52, 0xa6, 0x5e, 0xb0, 0xb9, 0xb9, 0x8a, 0x3e, 0x10, 0x3c, 0x77, 0x76, 0x01, 0xf3, - 0x1c, 0x07, 0x41, 0xf3, 0xe3, 0xa3, 0x85, 0x89, 0xc0, 0x69, 0xd1, 0x2b, 0xe1, 0x04, 0x4b, 0x6c, - 0xb2, 0x04, 0x45, 0x7e, 0x4b, 0x1b, 0xa9, 0xc2, 0x28, 0xa8, 0x0b, 0x7c, 0xdb, 0x10, 0x57, 0xba, - 0x07, 0x74, 0x3b, 0xcc, 0xf2, 0x9c, 0xc0, 0x27, 0xcb, 0x32, 0x39, 0xba, 0xda, 0x49, 0x88, 0x6c, - 0x33, 0xf1, 0x05, 0xcc, 0xfa, 0x9a, 0xa4, 0x20, 0x25, 0x98, 0x28, 0xbb, 0xad, 0x8e, 0x1d, 0x38, - 0xf8, 0xd2, 0xd0, 0xa1, 0x90, 0xc9, 0x68, 0x5f, 0xaa, 0xab, 0x05, 0x9a, 0x80, 0x57, 0x0b, 0xc8, - 0x4d, 0x98, 0x34, 0xdd, 0x2e, 0x9b, 0x24, 0x79, 0x28, 0xe4, 0x62, 0x17, 0x3d, 0x15, 0x3c, 0x56, - 0xc2, 0x76, 0x09, 0x71, 0x02, 0xd4, 0x72, 0xd4, 0x69, 0x54, 0x64, 0x2d, 0xc5, 0x3a, 0xaf, 0xca, - 0x5a, 0x35, 0xd7, 0x73, 0x82, 0x59, 0x8a, 0x61, 0xff, 0x3a, 0x8c, 0xd5, 0x6a, 0xeb, 0x9b, 0xd4, - 0x0f, 0x6e, 0x36, 0xdd, 0x03, 0x14, 0xb5, 0x05, 0xf1, 0x06, 0x86, 0xef, 0x5a, 0x01, 0xf5, 0x03, - 0x6b, 0xa7, 0xe9, 0x1e, 0x98, 0x2a, 0x16, 0xf9, 0x3a, 0x1b, 0x0f, 0x45, 0x31, 0x11, 0xd9, 0xf8, - 0xfa, 0xe9, 0x4e, 0x28, 0xd0, 0xa2, 0x4f, 0x86, 0x69, 0x50, 0xfa, 0x60, 0x29, 0xe8, 0x18, 0x94, - 0xc3, 0x8e, 0xb3, 0xa5, 0x46, 0xc3, 0xa3, 0xbe, 0x2f, 0x64, 0x22, 0x0f, 0xca, 0xc1, 0xb3, 0xaf, - 0xcd, 0x0b, 0xb4, 0xa0, 0x1c, 0x85, 0x80, 0x7c, 0x27, 0x03, 0xa7, 0x54, 0xbf, 0x7e, 0xfc, 0x58, - 0xd0, 0xeb, 0x82, 0x4b, 0xc8, 0xd7, 0xaf, 0xc8, 0x3d, 0xe1, 0x8a, 0x82, 0x76, 0xe5, 0xc1, 0xb5, - 0x2b, 0xa5, 0xe8, 0x67, 0x4d, 0x12, 0x89, 0x84, 0x56, 0x69, 0xfc, 0x54, 0xf9, 0x6e, 0xa7, 0x90, - 0x92, 0x32, 0x53, 0x1b, 0xd8, 0x7a, 0x42, 0x2f, 0x9e, 0xea, 0x06, 0x0a, 0x58, 0x61, 0xde, 0x13, - 0xab, 0x8f, 0xfb, 0xfb, 0x38, 0x1d, 0x5d, 0x3b, 0x50, 0x68, 0x50, 0xd9, 0xae, 0x95, 0xee, 0xae, - 0x46, 0x1a, 0xe3, 0x67, 0xcb, 0x99, 0x5e, 0xeb, 0x5b, 0x1f, 0x67, 0xfa, 0x7b, 0x3c, 0xbc, 0x50, - 0x19, 0x06, 0xa9, 0x6c, 0x6b, 0xe0, 0xb8, 0xb2, 0x1d, 0xa3, 0x31, 0x63, 0xd8, 0xc6, 0xc7, 0x85, - 0x18, 0x5f, 0xe1, 0x40, 0x67, 0xc0, 0x30, 0xd7, 0xa5, 0xd5, 0x27, 0xb2, 0xb9, 0xa6, 0x6d, 0x8a, - 0x12, 0x72, 0x16, 0x72, 0xb5, 0xda, 0xba, 0x18, 0x64, 0x74, 0xa3, 0xf3, 0x7d, 0xd7, 0x64, 0x30, - 0x36, 0x43, 0xe8, 0x1b, 0xa7, 0x24, 0xcf, 0x65, 0x62, 0xcf, 0x44, 0x28, 0x1b, 0x6f, 0xa9, 0xd9, - 0xe6, 0xa3, 0xf1, 0x16, 0x9a, 0x6d, 0xa4, 0xcf, 0x96, 0x61, 0xae, 0xe4, 0xfb, 0xd4, 0x63, 0xab, - 0x4a, 0xb8, 0x5c, 0x79, 0x42, 0xfb, 0x12, 0xd2, 0x1d, 0x2b, 0xb5, 0xeb, 0xbe, 0xd9, 0x13, 0x91, - 0x5c, 0x82, 0x42, 0xa9, 0xdb, 0x70, 0x68, 0xbb, 0xae, 0x65, 0xc6, 0xb1, 0x05, 0xcc, 0x0c, 0x4b, - 0xc9, 0x97, 0xe1, 0x54, 0x2c, 0x3b, 0x94, 0x18, 0x81, 0x91, 0xe8, 0x13, 0x94, 0xda, 0x61, 0x74, - 0x4d, 0xcc, 0x87, 0x24, 0x9d, 0x92, 0x94, 0xa0, 0xb8, 0x8c, 0xc1, 0x23, 0x15, 0xca, 0x2d, 0xd6, - 0xae, 0xc7, 0x03, 0x62, 0xb8, 0x2e, 0xcf, 0x03, 0x4b, 0xac, 0x46, 0x58, 0x68, 0x26, 0xd0, 0xc9, - 0x1d, 0x98, 0x89, 0xc3, 0x98, 0x20, 0xe7, 0x6a, 0x3b, 0x66, 0x6f, 0x4c, 0x70, 0x41, 0x51, 0x9e, - 0x46, 0x45, 0xb6, 0x61, 0x3a, 0x72, 0x93, 0xd0, 0x95, 0x79, 0xe9, 0x7d, 0x19, 0x96, 0x4b, 0x85, - 0xfe, 0x59, 0xb1, 0x18, 0x67, 0x22, 0x97, 0x8b, 0x50, 0xa9, 0x37, 0x93, 0xec, 0x48, 0x03, 0x26, - 0x6b, 0xce, 0x6e, 0xdb, 0x69, 0xef, 0xde, 0xa1, 0x87, 0x1b, 0xb6, 0xe3, 0x09, 0x3f, 0x38, 0xe9, - 0xe5, 0x5a, 0xf2, 0x0f, 0x5b, 0x2d, 0x1a, 0x78, 0xb8, 0x45, 0xb2, 0x72, 0x8c, 0xe5, 0x64, 0x4a, - 0xda, 0xbc, 0xcf, 0xe9, 0x30, 0x4e, 0xaa, 0x63, 0x3b, 0xda, 0x5e, 0xa0, 0xf3, 0xd4, 0x0e, 0x54, - 0xe3, 0x03, 0x1e, 0xa8, 0x9a, 0x30, 0xbd, 0xdc, 0xae, 0x7b, 0x87, 0x78, 0x71, 0x20, 0x1b, 0x37, - 0x71, 0x4c, 0xe3, 0x5e, 0x14, 0x8d, 0x7b, 0xce, 0x96, 0x2b, 0x2c, 0xad, 0x79, 0x49, 0xc6, 0xa4, - 0x06, 0xd3, 0xa8, 0xf5, 0x56, 0x2b, 0x1b, 0xd5, 0xb6, 0x13, 0x38, 0xf8, 0x90, 0x33, 0xdf, 0x63, - 0x5e, 0x12, 0x3c, 0xcf, 0x71, 0xc5, 0xd9, 0x69, 0x74, 0x2c, 0x47, 0xa2, 0xa8, 0x4c, 0x13, 0xf4, - 0xfd, 0xb4, 0xd7, 0xa9, 0x7f, 0x39, 0xda, 0x2b, 0x3e, 0x75, 0x14, 0x8b, 0x71, 0x2e, 0x46, 0x59, - 0xf3, 0x7d, 0x2c, 0x62, 0x72, 0xdd, 0xed, 0xa2, 0x4e, 0xa1, 0x3d, 0x75, 0xa4, 0xd3, 0x19, 0xff, - 0x51, 0x81, 0xcb, 0x76, 0x55, 0xe9, 0xec, 0xe5, 0xff, 0x16, 0x53, 0x46, 0xb3, 0x27, 0x51, 0x46, - 0x73, 0xc7, 0x2b, 0xa3, 0xf9, 0xe3, 0x94, 0xd1, 0x98, 0xb6, 0x38, 0x74, 0x62, 0x6d, 0x71, 0xf8, - 0x04, 0xda, 0xe2, 0xc8, 0x89, 0xb4, 0x45, 0x4d, 0xed, 0x2d, 0x1c, 0xa7, 0xf6, 0xfe, 0x85, 0x6e, - 0xf9, 0xb4, 0xea, 0x96, 0x69, 0xaa, 0xc2, 0x89, 0x74, 0xcb, 0xde, 0xaa, 0x61, 0xf1, 0xcf, 0x5a, - 0x35, 0x9c, 0xfe, 0x04, 0xaa, 0xe1, 0xf7, 0x41, 0x31, 0xbe, 0x5b, 0x1d, 0x9f, 0xc9, 0xee, 0x89, - 0x65, 0x5d, 0x62, 0x7b, 0x69, 0x7c, 0xb7, 0x60, 0x47, 0xd6, 0x0d, 0xcf, 0x79, 0x60, 0x07, 0xf4, - 0x8e, 0xbc, 0xe4, 0x17, 0x59, 0x18, 0x39, 0x14, 0xbf, 0x79, 0x05, 0x25, 0x54, 0x94, 0xb2, 0x69, - 0x8a, 0x92, 0xf1, 0x23, 0x59, 0x98, 0xe6, 0x19, 0x52, 0x9e, 0x7e, 0x5b, 0xf3, 0xbb, 0x9a, 0xfa, - 0x2b, 0x5d, 0xca, 0x62, 0xbd, 0xeb, 0x63, 0x6d, 0xfe, 0x1a, 0x9c, 0x4a, 0x0c, 0x05, 0xaa, 0xc0, - 0x15, 0x99, 0x9b, 0x26, 0xa1, 0x04, 0xcf, 0xa5, 0x57, 0x72, 0xff, 0xba, 0x99, 0xa0, 0x30, 0xfe, - 0x79, 0x3e, 0xc1, 0x5f, 0xd8, 0x9d, 0x55, 0x4b, 0x72, 0xe6, 0x64, 0x96, 0xe4, 0xec, 0x60, 0x96, - 0xe4, 0xd8, 0xde, 0x92, 0x1b, 0x64, 0x6f, 0xf9, 0x32, 0x4c, 0x6c, 0x52, 0xbb, 0xe5, 0x6f, 0xba, - 0x22, 0xd5, 0x3a, 0x77, 0x29, 0x95, 0xa9, 0x67, 0x58, 0x99, 0xd4, 0xe0, 0x42, 0xd7, 0x98, 0x80, - 0x11, 0x30, 0x79, 0xc8, 0x73, 0xaf, 0x9b, 0x3a, 0x07, 0x55, 0x2d, 0x1f, 0xea, 0xa3, 0x96, 0xd7, - 0x60, 0x5c, 0xd0, 0x45, 0xe9, 0xfb, 0x22, 0xfd, 0x91, 0x15, 0x21, 0x5c, 0xd6, 0x1e, 0x3e, 0x86, - 0x17, 0xd6, 0xce, 0x55, 0x47, 0x8d, 0x09, 0x1b, 0x82, 0xe5, 0x76, 0xa3, 0xe3, 0x3a, 0x6d, 0x1c, - 0x82, 0x91, 0x68, 0x08, 0xa8, 0x00, 0xf3, 0x21, 0x50, 0x90, 0xc8, 0xdb, 0x30, 0x59, 0xda, 0xa8, - 0xaa, 0x64, 0x85, 0xc8, 0x98, 0x6d, 0x77, 0x1c, 0x4b, 0x23, 0x8d, 0xe1, 0xf6, 0x53, 0xa5, 0x46, - 0xff, 0xe5, 0xa8, 0x52, 0xc6, 0xb7, 0x47, 0xe5, 0xe7, 0xfd, 0xe9, 0x9a, 0xdd, 0x74, 0x43, 0x5a, - 0xee, 0x84, 0x86, 0xb4, 0xfc, 0x71, 0x1a, 0x85, 0xa6, 0xe6, 0x0c, 0x9d, 0x40, 0xcd, 0x19, 0x7e, - 0x6c, 0xa3, 0xd8, 0xc8, 0x09, 0x15, 0x97, 0xd8, 0x97, 0x56, 0x18, 0xe4, 0x4b, 0x4b, 0x55, 0x76, - 0x46, 0x1f, 0x5f, 0xd9, 0x81, 0x13, 0x2b, 0x3b, 0xb5, 0x28, 0x40, 0x6b, 0xec, 0x58, 0xbf, 0xd7, - 0x73, 0xe2, 0x90, 0x31, 0x9d, 0x9e, 0x1c, 0x27, 0x0c, 0xd5, 0xfa, 0x4c, 0x69, 0x50, 0xdf, 0x48, - 0xd7, 0xa0, 0xfa, 0xef, 0x36, 0xff, 0x7f, 0xd6, 0xa1, 0x3c, 0x1c, 0xe5, 0x2d, 0xdb, 0x6b, 0xe3, - 0xe1, 0xee, 0x2a, 0x8c, 0xc8, 0x24, 0x51, 0x99, 0xc8, 0x4e, 0x91, 0xcc, 0x0e, 0x25, 0xb1, 0xd8, - 0x39, 0x5c, 0x12, 0xab, 0x09, 0xaf, 0x0f, 0x04, 0x4c, 0xcb, 0xbf, 0x23, 0x60, 0xc6, 0xdf, 0xcb, - 0xcb, 0x2f, 0x99, 0x9d, 0xac, 0xc4, 0xe3, 0xc8, 0x4b, 0xca, 0xcc, 0x29, 0x1a, 0x5c, 0x6c, 0x6e, - 0x62, 0x6e, 0x6c, 0x3a, 0xc9, 0x27, 0x4a, 0xdb, 0x15, 0x3d, 0xca, 0x94, 0x1b, 0xe0, 0x51, 0xa6, - 0x37, 0xb5, 0x17, 0x8d, 0xf2, 0xd1, 0x13, 0x1a, 0x6c, 0x75, 0xf7, 0x7f, 0xcb, 0xe8, 0x86, 0xfa, - 0xf4, 0xd0, 0x50, 0x94, 0xc1, 0x02, 0x29, 0xfb, 0x3c, 0x3a, 0x14, 0xaa, 0xa4, 0xc3, 0x27, 0x49, - 0x88, 0x37, 0xf2, 0x67, 0x9a, 0x10, 0x6f, 0x19, 0x40, 0x79, 0x90, 0x96, 0xdf, 0x7d, 0xbc, 0xc4, - 0x86, 0xe9, 0xf8, 0xc7, 0x68, 0x15, 0x42, 0xe3, 0x77, 0x09, 0x4c, 0xd7, 0x6a, 0xeb, 0x15, 0xc7, - 0xde, 0x6d, 0xbb, 0x7e, 0xe0, 0xd4, 0xab, 0xed, 0x1d, 0x97, 0xe9, 0x63, 0xa1, 0x54, 0x50, 0x32, - 0x9f, 0x45, 0x12, 0x21, 0x2c, 0x66, 0xfa, 0xfe, 0xb2, 0xe7, 0xb9, 0x9e, 0xaa, 0xef, 0x53, 0x06, - 0x30, 0x39, 0x9c, 0xa9, 0x3c, 0xb5, 0x2e, 0x7f, 0x59, 0x94, 0x5f, 0x47, 0xa1, 0xca, 0xe3, 0x73, - 0x90, 0x29, 0xcb, 0x08, 0x4d, 0x2e, 0x58, 0xa1, 0x02, 0x9f, 0xd1, 0xd2, 0xea, 0x45, 0xc5, 0x5c, - 0xe6, 0x89, 0x3d, 0x09, 0xe3, 0xa2, 0x3a, 0x08, 0x57, 0x6f, 0x7f, 0x13, 0xdf, 0xc0, 0x21, 0x9c, - 0xd2, 0xe2, 0x7b, 0x06, 0x35, 0xd1, 0xbd, 0x22, 0x54, 0x2c, 0x03, 0xc3, 0x49, 0x53, 0xec, 0x74, - 0xea, 0x13, 0x00, 0xa9, 0x35, 0x90, 0x1f, 0xc9, 0xc0, 0xb9, 0xd4, 0x92, 0xf0, 0xeb, 0x1e, 0xd3, - 0x52, 0x1b, 0x2a, 0x42, 0x83, 0x3f, 0x76, 0xd0, 0xab, 0x6a, 0x2b, 0x45, 0x14, 0xf4, 0xaf, 0x89, - 0xfc, 0x5a, 0x06, 0xce, 0x68, 0x18, 0xa1, 0xcc, 0xf3, 0xc3, 0xd0, 0xd7, 0xd4, 0x75, 0xfd, 0xd1, - 0x93, 0x59, 0xd7, 0x17, 0xf4, 0xbe, 0x44, 0x22, 0x59, 0xed, 0x43, 0xaf, 0x16, 0x92, 0x07, 0x30, - 0x8d, 0x45, 0xd2, 0x5c, 0xc8, 0xd6, 0xac, 0xb0, 0x32, 0xce, 0x46, 0xcd, 0xe6, 0x31, 0x6b, 0xf8, - 0x60, 0xdd, 0xe2, 0x77, 0x8f, 0x16, 0x26, 0x34, 0x74, 0x99, 0x2c, 0xd0, 0x8a, 0x6c, 0x8e, 0x4e, - 0x7b, 0xc7, 0x55, 0xf7, 0xcb, 0x44, 0x15, 0xe4, 0x3f, 0xc9, 0xc0, 0x1c, 0x83, 0xf2, 0x6e, 0xdc, - 0xf4, 0xdc, 0x56, 0x58, 0x2e, 0xdd, 0x08, 0x7a, 0x0c, 0x5b, 0xf3, 0xc9, 0x0c, 0xdb, 0x4b, 0xd8, - 0x64, 0x2e, 0x13, 0xac, 0x1d, 0xcf, 0x6d, 0x45, 0xcd, 0xd7, 0x1e, 0x5c, 0xed, 0xd5, 0x48, 0xf2, - 0x03, 0x19, 0x38, 0xab, 0xd9, 0x38, 0xd4, 0x94, 0xca, 0x22, 0x32, 0x70, 0x26, 0x8c, 0x19, 0x8e, - 0x8a, 0x96, 0xae, 0x88, 0xf5, 0x7f, 0x11, 0x5b, 0x10, 0xed, 0x16, 0xd8, 0x16, 0xab, 0xc5, 0xb1, - 0x94, 0x26, 0xf4, 0xae, 0x85, 0x38, 0x30, 0x8d, 0x57, 0x78, 0x9a, 0xbb, 0xcb, 0x6c, 0x6f, 0x77, - 0x97, 0xf0, 0x61, 0x21, 0xcc, 0xd7, 0xda, 0xdb, 0xe7, 0x25, 0xc9, 0x95, 0xfc, 0x25, 0x38, 0x9b, - 0x00, 0x86, 0x5f, 0xdb, 0xa9, 0x9e, 0x5f, 0xdb, 0xab, 0x8f, 0x8e, 0x16, 0x5e, 0x4e, 0xab, 0x2d, - 0xed, 0x4b, 0xeb, 0x5d, 0x03, 0xb1, 0x01, 0xa2, 0x42, 0xf1, 0x6e, 0x6b, 0xfa, 0x02, 0x7d, 0x55, - 0xac, 0x0f, 0x05, 0x9f, 0xc9, 0x72, 0xa5, 0x0d, 0xea, 0x96, 0x17, 0x21, 0x11, 0x0a, 0xe3, 0x4a, - 0xae, 0xda, 0x43, 0x7c, 0xc0, 0xb5, 0x67, 0x25, 0xdf, 0x3d, 0x5a, 0xd0, 0xb0, 0x99, 0x5e, 0xac, - 0x26, 0xc1, 0x55, 0xf5, 0x62, 0x0d, 0x91, 0xfc, 0x4a, 0x06, 0x66, 0x19, 0x20, 0x5a, 0x54, 0xa2, - 0x53, 0x73, 0xfd, 0x56, 0xfd, 0xde, 0x93, 0x59, 0xf5, 0x2f, 0x60, 0x1b, 0xd5, 0x55, 0x9f, 0x18, - 0x92, 0xd4, 0xc6, 0xe1, 0x6a, 0xd7, 0x6e, 0x8b, 0xb5, 0xd5, 0x7e, 0x76, 0x80, 0xd5, 0xce, 0x27, - 0xe0, 0xf8, 0xd5, 0xde, 0xb3, 0x16, 0xb2, 0x09, 0xe3, 0x42, 0x25, 0xe6, 0x03, 0xf6, 0xbc, 0x96, - 0xe7, 0x52, 0x2d, 0xe2, 0xe7, 0x14, 0x91, 0xca, 0x37, 0xd1, 0x43, 0x8d, 0x0b, 0x69, 0xc3, 0x0c, - 0xff, 0xad, 0x1b, 0x28, 0x16, 0x7a, 0x1a, 0x28, 0x2e, 0x89, 0x1e, 0x9d, 0x17, 0xfc, 0x63, 0x76, - 0x0a, 0x35, 0xd5, 0x40, 0x0a, 0x63, 0xd2, 0x01, 0xa2, 0x81, 0xf9, 0x47, 0x7b, 0xbe, 0xbf, 0x59, - 0xe2, 0x65, 0x51, 0xe7, 0x42, 0xbc, 0xce, 0xf8, 0x97, 0x9b, 0xc2, 0x9b, 0xd8, 0x30, 0x25, 0xa0, - 0xec, 0x00, 0x8c, 0x12, 0xfe, 0x05, 0x2d, 0xd9, 0x43, 0xac, 0x94, 0xdf, 0x8d, 0xc8, 0x9a, 0x30, - 0x19, 0x47, 0x4c, 0xa0, 0xc7, 0xf9, 0x91, 0x75, 0x98, 0x2e, 0x75, 0x3a, 0x4d, 0x87, 0x36, 0xb0, - 0x97, 0xfc, 0x0d, 0x4e, 0x23, 0x7a, 0x77, 0xc1, 0xe6, 0x85, 0x42, 0xc5, 0x8f, 0x3f, 0xc0, 0x99, - 0xa4, 0x35, 0xbe, 0x93, 0x49, 0x34, 0x9a, 0x9d, 0xdc, 0xf1, 0x87, 0x12, 0x3f, 0x8c, 0x27, 0x77, - 0xde, 0x44, 0xb4, 0x20, 0x44, 0x08, 0x4c, 0x59, 0x52, 0x73, 0x08, 0xe5, 0xb8, 0xb2, 0x24, 0x8e, - 0x97, 0xd1, 0x81, 0x72, 0x41, 0xba, 0x21, 0xe6, 0x22, 0xa5, 0x0b, 0xdd, 0x10, 0x85, 0xf3, 0xa1, - 0xf1, 0x03, 0x59, 0x7d, 0xd9, 0x91, 0x4b, 0x8a, 0xde, 0xae, 0x64, 0x31, 0x92, 0x7a, 0xbb, 0xa2, - 0xad, 0xff, 0xdd, 0x0c, 0xcc, 0xac, 0x7b, 0xbb, 0x76, 0xdb, 0xf9, 0x16, 0xcf, 0x86, 0xe8, 0xe2, - 0xbc, 0xf4, 0x7f, 0xc2, 0xe6, 0x49, 0x3d, 0xc5, 0xe1, 0x2a, 0x15, 0xb3, 0x95, 0x82, 0x4b, 0xc6, - 0x4c, 0x6b, 0x0f, 0x3a, 0x76, 0x63, 0xc3, 0x94, 0x17, 0x51, 0x38, 0x3a, 0x87, 0x1b, 0x3f, 0x96, - 0x85, 0x31, 0xe5, 0x13, 0x20, 0x9f, 0x87, 0x71, 0x95, 0x8f, 0x6a, 0xf5, 0x51, 0xab, 0x35, 0x35, - 0x2c, 0x34, 0xfb, 0x50, 0xbb, 0xa5, 0x99, 0x7d, 0xd8, 0x42, 0x47, 0xe8, 0x09, 0x8f, 0x36, 0xef, - 0xa5, 0x1c, 0x6d, 0x4e, 0xf4, 0x58, 0xeb, 0xdb, 0xc9, 0x03, 0xce, 0xe0, 0x6f, 0xab, 0x1a, 0x3f, - 0x99, 0x81, 0x62, 0xfc, 0x23, 0xfd, 0x54, 0x46, 0xe5, 0x04, 0x26, 0xfe, 0x1f, 0xcd, 0x86, 0x89, - 0xaa, 0x65, 0xb8, 0xca, 0xd3, 0xea, 0x7c, 0xf2, 0x8e, 0x66, 0x7d, 0x7f, 0x56, 0xcf, 0xbc, 0xa2, - 0x06, 0x7a, 0xa6, 0xa7, 0x5b, 0xca, 0xff, 0xec, 0xcf, 0x2f, 0x3c, 0x63, 0x7c, 0x00, 0xb3, 0xf1, - 0xe1, 0x40, 0x0b, 0x7c, 0x09, 0xa6, 0x74, 0x78, 0x3c, 0xcd, 0x7d, 0x9c, 0xca, 0x8c, 0xe3, 0x1b, - 0xbf, 0x97, 0x8d, 0xf3, 0x16, 0x8e, 0x28, 0x4c, 0xe8, 0xb4, 0xed, 0xed, 0x66, 0x98, 0xe6, 0x9a, - 0x0b, 0x1d, 0x0e, 0x32, 0x65, 0xd9, 0x49, 0xde, 0x7d, 0x08, 0x83, 0x2e, 0x72, 0xe9, 0x41, 0x17, - 0xe4, 0x46, 0xcc, 0x05, 0x4b, 0xc9, 0x10, 0x70, 0x40, 0xb7, 0xad, 0xc8, 0x0d, 0x2b, 0xe6, 0x79, - 0x55, 0x86, 0x59, 0x2d, 0xdd, 0xa5, 0xa4, 0x1f, 0x8a, 0x0c, 0xae, 0x01, 0x16, 0x70, 0xe2, 0x54, - 0x64, 0xb2, 0x02, 0x23, 0xac, 0x99, 0x77, 0xed, 0x8e, 0x30, 0xac, 0x93, 0x30, 0x04, 0xab, 0x19, - 0x1e, 0xf8, 0x94, 0x28, 0xac, 0x26, 0x65, 0x5b, 0xbe, 0xf6, 0xd6, 0x31, 0x47, 0x34, 0xfe, 0x45, - 0x86, 0x7d, 0xff, 0xf5, 0xfd, 0xcf, 0xd8, 0xe3, 0x11, 0xac, 0x4b, 0x7d, 0xfc, 0xa4, 0xfe, 0x30, - 0xcb, 0x33, 0x95, 0x8b, 0xe5, 0xf3, 0x26, 0x0c, 0x6f, 0xda, 0xde, 0x2e, 0x0d, 0x44, 0x0e, 0x6f, - 0x95, 0x0b, 0x2f, 0x88, 0xf2, 0x17, 0x04, 0xf8, 0xdb, 0x14, 0x04, 0xaa, 0x2d, 0x2c, 0x3b, 0x90, - 0x2d, 0x4c, 0x31, 0xcf, 0xe6, 0x9e, 0x98, 0x79, 0xf6, 0x7b, 0xc2, 0xa4, 0xe4, 0xa5, 0x60, 0x80, - 0x6c, 0x8a, 0xe7, 0xe3, 0x49, 0xfd, 0x13, 0x79, 0x2f, 0x23, 0x76, 0xe4, 0x86, 0xfa, 0x4c, 0x80, - 0x12, 0xc7, 0x70, 0xcc, 0x83, 0x00, 0xc6, 0x1f, 0xe6, 0xf8, 0x18, 0x8b, 0x81, 0xba, 0xa8, 0xc5, - 0x38, 0xe1, 0x77, 0xc2, 0x04, 0xbd, 0x1a, 0x6e, 0x8a, 0x2e, 0x14, 0x17, 0x21, 0xcf, 0xd6, 0xa6, - 0x18, 0x4d, 0xc4, 0x63, 0xeb, 0x57, 0xc5, 0x63, 0xe5, 0xec, 0x5b, 0xc6, 0x3d, 0x49, 0x7d, 0x98, - 0x05, 0xb7, 0x2d, 0xf5, 0x5b, 0x46, 0x0c, 0x72, 0x09, 0xf2, 0x6b, 0x6e, 0x43, 0x66, 0xed, 0x9c, - 0xc5, 0x48, 0x57, 0xed, 0xf1, 0xfe, 0xb9, 0x8c, 0x89, 0x18, 0xac, 0xaf, 0x61, 0x9e, 0x6f, 0xb5, - 0xaf, 0xad, 0x1d, 0x5b, 0xa4, 0x96, 0x52, 0xfb, 0x1a, 0xa5, 0x04, 0x5f, 0x86, 0x49, 0xfd, 0x69, - 0x46, 0xe1, 0x45, 0x86, 0x66, 0xd6, 0xd8, 0x0b, 0x8f, 0xaa, 0x75, 0x5c, 0x27, 0x22, 0x4b, 0x30, - 0xa1, 0x65, 0x0b, 0x13, 0x37, 0x5c, 0x68, 0xde, 0xd4, 0x73, 0x8d, 0xa9, 0xe6, 0x4d, 0x8d, 0x84, - 0xed, 0xe7, 0xa2, 0xfd, 0xca, 0x3d, 0x57, 0xa2, 0xed, 0x02, 0x87, 0x5c, 0x87, 0x02, 0x0f, 0x29, - 0xad, 0x56, 0xd4, 0xdb, 0x0a, 0x1f, 0x61, 0xb1, 0x90, 0x6c, 0x89, 0xa8, 0x84, 0x10, 0x7e, 0x0e, - 0x8a, 0x42, 0x24, 0x45, 0x8f, 0x20, 0x3e, 0x07, 0xf9, 0x72, 0xb5, 0x62, 0xaa, 0x62, 0xa4, 0xee, - 0x34, 0x3c, 0x13, 0xa1, 0xc6, 0x4f, 0x65, 0xe0, 0xec, 0x1a, 0x0d, 0x0e, 0x5c, 0x6f, 0xdf, 0xa4, - 0x7e, 0xe0, 0x39, 0xfc, 0x5d, 0x1f, 0xfc, 0x10, 0x3f, 0x4f, 0xde, 0x86, 0x21, 0x74, 0x67, 0x8a, - 0xed, 0x0c, 0xf1, 0x3a, 0x96, 0x26, 0xc4, 0x02, 0x1e, 0x42, 0xdf, 0x28, 0x93, 0x13, 0x91, 0x37, - 0x21, 0x5f, 0xa1, 0xed, 0xc3, 0xd8, 0xcb, 0x26, 0x09, 0xe2, 0x50, 0x20, 0x34, 0x68, 0xfb, 0xd0, - 0x44, 0x12, 0xe3, 0x27, 0xb3, 0x70, 0x2a, 0xa5, 0x59, 0xf7, 0x3f, 0xff, 0x94, 0x4a, 0xc5, 0x25, - 0x4d, 0x2a, 0xca, 0x4b, 0xca, 0x9e, 0x03, 0x9f, 0x2a, 0x24, 0xff, 0x56, 0x06, 0xce, 0xe8, 0x0b, - 0x54, 0xf8, 0x2f, 0xde, 0xbf, 0x4e, 0xde, 0x82, 0xe1, 0x15, 0x6a, 0x37, 0xa8, 0x7c, 0xf5, 0xe0, - 0x54, 0x98, 0xfc, 0x85, 0xc7, 0xcb, 0xf1, 0x42, 0xce, 0x36, 0x8a, 0xae, 0xe0, 0x50, 0x52, 0x11, - 0x8d, 0xe3, 0xfa, 0xb8, 0x21, 0x63, 0x57, 0xd3, 0xaa, 0xea, 0x73, 0xd5, 0xff, 0xdd, 0x0c, 0x3c, - 0xdb, 0x87, 0x86, 0x4d, 0x1c, 0x9b, 0x7a, 0x75, 0xe2, 0x70, 0x47, 0x45, 0x28, 0x79, 0x17, 0xa6, - 0x36, 0x85, 0x3e, 0x2f, 0xa7, 0x23, 0x1b, 0x7d, 0x2f, 0x52, 0xd5, 0xb7, 0xe4, 0xbc, 0xc4, 0x91, - 0xb5, 0xa0, 0xea, 0x5c, 0xdf, 0xa0, 0x6a, 0x35, 0x46, 0x39, 0x3f, 0x68, 0x8c, 0xf2, 0x07, 0xf1, - 0x07, 0xcd, 0x45, 0xaa, 0xb8, 0x28, 0x42, 0x3b, 0xd3, 0x3b, 0x42, 0xbb, 0x6f, 0x42, 0x2a, 0xe3, - 0xc7, 0x32, 0x50, 0xd4, 0x79, 0x3f, 0xee, 0x7c, 0xbe, 0xa3, 0xcd, 0xe7, 0xb3, 0xe9, 0xf3, 0xd9, - 0x7b, 0x22, 0xff, 0xb7, 0x4c, 0xbc, 0xb3, 0x03, 0xcd, 0xa0, 0x01, 0xc3, 0x15, 0xb7, 0x65, 0x3b, - 0x6d, 0xf5, 0x4d, 0xcd, 0x06, 0x42, 0x4c, 0x51, 0x32, 0x58, 0x40, 0xfb, 0x79, 0x18, 0x5a, 0x73, - 0xdb, 0xa5, 0x8a, 0x70, 0xef, 0x43, 0x3e, 0x6d, 0xb7, 0x6d, 0xd9, 0x0d, 0x93, 0x17, 0x90, 0x55, - 0x80, 0x5a, 0xdd, 0xa3, 0xb4, 0x5d, 0x73, 0xbe, 0x45, 0x63, 0x9a, 0x06, 0x1b, 0xa1, 0x66, 0x17, - 0x05, 0x0b, 0xde, 0xf1, 0xf8, 0x88, 0x68, 0xf9, 0xce, 0xb7, 0x54, 0x79, 0xab, 0xd0, 0x1b, 0x14, - 0x20, 0x22, 0xc2, 0x07, 0xc6, 0x9c, 0x86, 0x78, 0x34, 0x76, 0x42, 0x3c, 0x30, 0xc6, 0x00, 0xda, - 0x03, 0x63, 0x0c, 0xc0, 0x44, 0xfb, 0x0a, 0x75, 0x76, 0xf7, 0xb8, 0xcb, 0xc8, 0x04, 0x5f, 0xaa, - 0x7b, 0x08, 0x51, 0x45, 0x3b, 0xc7, 0x31, 0x7e, 0x6b, 0x08, 0xce, 0x9a, 0x74, 0xd7, 0x61, 0x6a, - 0xf2, 0x3d, 0xdf, 0x69, 0xef, 0x6a, 0x21, 0xc7, 0x46, 0x6c, 0x21, 0x89, 0xfc, 0xbc, 0x0c, 0x12, - 0x0e, 0xcc, 0x65, 0x28, 0xb0, 0x5d, 0x51, 0x59, 0x4b, 0x78, 0x87, 0x82, 0x2f, 0x62, 0xf3, 0x45, - 0x2e, 0x8b, 0xc9, 0x2b, 0x62, 0xd7, 0x56, 0x32, 0xa8, 0xb3, 0x5d, 0xfb, 0xe3, 0xa3, 0x05, 0xa8, - 0x1d, 0xfa, 0x01, 0xc5, 0x13, 0x9b, 0xd8, 0xb9, 0x43, 0xd5, 0x3a, 0xdf, 0x43, 0xb5, 0xbe, 0x0b, - 0xb3, 0xa5, 0x06, 0x17, 0xd6, 0x76, 0x73, 0xc3, 0x73, 0xda, 0x75, 0xa7, 0x63, 0x37, 0xe5, 0x71, - 0x11, 0x47, 0xd9, 0x0e, 0xcb, 0xad, 0x4e, 0x88, 0x60, 0xa6, 0x92, 0xb1, 0x6e, 0x54, 0xd6, 0x6a, - 0x18, 0x99, 0x2b, 0xae, 0xc7, 0xb0, 0x1b, 0x8d, 0xb6, 0x8f, 0xbd, 0xf0, 0xcd, 0xb0, 0x18, 0x95, - 0x7a, 0xf4, 0x41, 0xd8, 0x5c, 0xad, 0x45, 0xc1, 0x3b, 0x3c, 0xc1, 0x2b, 0xf7, 0x53, 0x08, 0x9a, - 0x3e, 0xfa, 0x2a, 0x68, 0x78, 0x11, 0x5d, 0xad, 0xb6, 0xc2, 0xe8, 0x0a, 0x09, 0x3a, 0xdf, 0xdf, - 0x53, 0xe9, 0x38, 0x1e, 0xb9, 0xca, 0x96, 0x42, 0xcb, 0x0d, 0x28, 0xae, 0xf3, 0xd1, 0xe8, 0x08, - 0xe0, 0x21, 0x94, 0x1f, 0x01, 0x14, 0x14, 0xf2, 0x36, 0xcc, 0x2c, 0x97, 0x17, 0xa5, 0x51, 0xb3, - 0xe2, 0xd6, 0xbb, 0x78, 0xab, 0x0c, 0x58, 0x1f, 0xce, 0x21, 0xad, 0x2f, 0xb2, 0xc5, 0x9d, 0x86, - 0x46, 0x2e, 0xc2, 0x48, 0xb5, 0xc2, 0xc7, 0x7e, 0x4c, 0x7d, 0xc5, 0x40, 0x78, 0x6b, 0xc8, 0x42, - 0xb2, 0x1e, 0xe9, 0xa8, 0xe3, 0xc7, 0x2a, 0x93, 0x67, 0x07, 0xd0, 0x4f, 0xdf, 0x84, 0x89, 0x25, - 0x37, 0xa8, 0xb6, 0xfd, 0xc0, 0x6e, 0xd7, 0x69, 0xb5, 0xa2, 0xa6, 0x14, 0xdc, 0x76, 0x03, 0xcb, - 0x11, 0x25, 0xac, 0xe5, 0x3a, 0xa6, 0x78, 0x27, 0x81, 0xbf, 0xc7, 0x53, 0x76, 0x1b, 0xd4, 0xbf, - 0x7f, 0xed, 0x33, 0xf6, 0x4e, 0x82, 0xd2, 0x37, 0x14, 0x7c, 0xd7, 0x52, 0xa5, 0xe4, 0xbf, 0x81, - 0xef, 0x24, 0x24, 0x70, 0xc9, 0x17, 0x61, 0x08, 0x7f, 0x0a, 0x95, 0x69, 0x26, 0x85, 0x6d, 0xa4, - 0x2e, 0xd5, 0xf9, 0x73, 0xba, 0x48, 0x40, 0xaa, 0x30, 0x22, 0xb4, 0xf5, 0x93, 0x64, 0xfb, 0x16, - 0x6a, 0x3f, 0x9f, 0x5f, 0x41, 0x6f, 0x34, 0x60, 0x5c, 0xad, 0x90, 0xad, 0xeb, 0x15, 0xdb, 0xdf, - 0xa3, 0x0d, 0xf6, 0x4b, 0x3c, 0xd4, 0x81, 0xeb, 0x7a, 0x0f, 0xa1, 0x16, 0x6b, 0x87, 0xa9, 0xa0, - 0x30, 0x41, 0x5d, 0xf5, 0xef, 0xf9, 0xa2, 0x29, 0xe2, 0xfc, 0xee, 0xa0, 0x2d, 0xa8, 0x61, 0x8a, - 0x22, 0xe3, 0x7b, 0x60, 0x76, 0xad, 0xdb, 0x6c, 0xb2, 0xb3, 0xbc, 0x4c, 0xe4, 0x1c, 0xd8, 0x01, - 0x25, 0x4b, 0x30, 0x54, 0x53, 0x1e, 0xe8, 0x9b, 0x09, 0x33, 0x65, 0x47, 0x38, 0xe8, 0xde, 0x96, - 0xc1, 0x58, 0xe7, 0xd8, 0xd3, 0x7c, 0x9c, 0xd4, 0xf8, 0x9d, 0xe8, 0x61, 0xe7, 0x4d, 0xcf, 0xae, - 0xef, 0x87, 0x8f, 0x34, 0x0e, 0xfa, 0x46, 0xf5, 0x6d, 0xd9, 0x08, 0x7d, 0x17, 0x4c, 0x6b, 0xf0, - 0x71, 0x8d, 0x21, 0x6f, 0xc3, 0x98, 0xd8, 0x09, 0x95, 0x0c, 0x3d, 0x98, 0x06, 0x41, 0xbe, 0x12, - 0x1f, 0xf3, 0x54, 0x50, 0xd1, 0x71, 0x83, 0xd7, 0xbb, 0x72, 0xff, 0xda, 0xa7, 0xb1, 0xc1, 0xeb, - 0x75, 0xf4, 0x59, 0xba, 0xbf, 0x31, 0x16, 0x1f, 0x5b, 0xb1, 0x76, 0x6f, 0xa8, 0x39, 0x39, 0x32, - 0xd1, 0x71, 0x2b, 0xca, 0xc9, 0xa1, 0x1e, 0xb7, 0x42, 0xd4, 0x70, 0x4e, 0xb2, 0xc7, 0xcc, 0xc9, - 0xbb, 0x72, 0x4e, 0x72, 0xbd, 0x17, 0xc6, 0x4c, 0x9f, 0x79, 0xa8, 0x45, 0x5f, 0x48, 0x7e, 0xa0, - 0xb3, 0xfa, 0x33, 0x98, 0x7c, 0x94, 0x93, 0xc4, 0x65, 0xa1, 0xe0, 0xa4, 0x1a, 0x00, 0x86, 0x06, - 0x67, 0x7a, 0x8c, 0x80, 0xfd, 0x12, 0x8c, 0x97, 0x82, 0xc0, 0xae, 0xef, 0xd1, 0x46, 0x85, 0x89, - 0x27, 0x25, 0x7d, 0x80, 0x2d, 0xe0, 0xea, 0x4d, 0x8c, 0x8a, 0xcb, 0xd3, 0x61, 0xd9, 0xbe, 0x70, - 0x94, 0x0b, 0xd3, 0x61, 0x31, 0x88, 0x9e, 0x0e, 0x8b, 0x41, 0xc8, 0x55, 0x18, 0xa9, 0xb6, 0x1f, - 0x38, 0x6c, 0x4c, 0x0a, 0xca, 0x53, 0xf4, 0x1c, 0xa4, 0x0a, 0x57, 0x81, 0x45, 0xde, 0x54, 0x34, - 0xe5, 0xd1, 0xe8, 0x54, 0xcc, 0xed, 0x28, 0x61, 0xe4, 0xb1, 0xaa, 0x05, 0x87, 0xaa, 0xf3, 0x0d, - 0x18, 0x91, 0xe6, 0x31, 0x88, 0x4e, 0xc2, 0x82, 0x32, 0x19, 0xe2, 0x28, 0x91, 0xf1, 0x5d, 0x3f, - 0xe5, 0xc1, 0x91, 0x31, 0xe5, 0x5d, 0x3f, 0xe5, 0xc1, 0x11, 0xed, 0x5d, 0x3f, 0xe5, 0xe9, 0x91, - 0xd0, 0xb2, 0x30, 0x7e, 0xac, 0x65, 0xe1, 0x3e, 0x8c, 0x6f, 0xd8, 0x5e, 0xe0, 0x30, 0x4d, 0xa3, - 0x1d, 0xf8, 0x73, 0x13, 0x9a, 0x31, 0x4e, 0x29, 0x5a, 0x7a, 0x5e, 0x3e, 0x45, 0xd7, 0x51, 0xf0, - 0xf5, 0x37, 0xd3, 0x22, 0x78, 0xba, 0x9b, 0xdc, 0xe4, 0xe3, 0xb8, 0xc9, 0xe1, 0xa0, 0xa2, 0x01, - 0x66, 0x2a, 0x3a, 0xe6, 0xa3, 0x26, 0x1c, 0xb3, 0xc2, 0x84, 0x88, 0xe4, 0xab, 0x30, 0xce, 0xfe, - 0xc6, 0xd7, 0xdf, 0x1d, 0xea, 0xcf, 0x15, 0xb1, 0x73, 0xcf, 0xa7, 0x7e, 0xfd, 0xfc, 0x89, 0xf8, - 0x1a, 0x0d, 0xf8, 0x07, 0x8c, 0x8c, 0xe3, 0x96, 0x55, 0x8d, 0x1b, 0x79, 0x0f, 0xc6, 0xd9, 0xea, - 0xdb, 0xb6, 0x7d, 0xae, 0x60, 0x4e, 0x47, 0x8e, 0x8e, 0x0d, 0x01, 0x4f, 0x64, 0xa4, 0x53, 0x09, - 0xd8, 0x36, 0x5f, 0xea, 0x70, 0x01, 0x49, 0x94, 0xd5, 0xde, 0x49, 0x08, 0x47, 0x89, 0x46, 0xde, - 0x87, 0xf1, 0x52, 0xa7, 0x13, 0x49, 0x9c, 0x19, 0xc5, 0xba, 0xd2, 0xe9, 0x58, 0xa9, 0x52, 0x47, - 0xa3, 0x88, 0x0b, 0xe6, 0xd9, 0x13, 0x09, 0x66, 0xf2, 0x7a, 0xa8, 0x73, 0x9f, 0x8a, 0x4c, 0x85, - 0xe2, 0x34, 0xa2, 0x29, 0xf0, 0x5c, 0xfd, 0x2e, 0xc3, 0x04, 0xb7, 0x9d, 0x49, 0x6d, 0xe6, 0x74, - 0xe2, 0xeb, 0x49, 0x51, 0x6a, 0x74, 0x1a, 0xb2, 0x0c, 0x93, 0x3c, 0x30, 0xac, 0x29, 0x52, 0x05, - 0xce, 0x9d, 0x89, 0xde, 0x18, 0xe6, 0xf1, 0x64, 0x4d, 0xcc, 0x20, 0x6d, 0x6b, 0x5c, 0x62, 0x44, - 0xc6, 0x1f, 0x65, 0xe0, 0x4c, 0x8f, 0x19, 0x0f, 0x13, 0xc9, 0x65, 0xfa, 0x27, 0x92, 0x63, 0x92, - 0x43, 0x3f, 0x6a, 0x63, 0xff, 0x85, 0x96, 0xa5, 0xce, 0x97, 0xd4, 0xb7, 0x5c, 0x20, 0x22, 0x49, - 0xbb, 0xa8, 0xfa, 0xb6, 0x8b, 0xf6, 0xbe, 0x5c, 0x72, 0x13, 0x12, 0x78, 0xbc, 0x51, 0x4b, 0xc6, - 0xa3, 0xa3, 0x85, 0xe7, 0x45, 0x0e, 0xf8, 0x70, 0x5a, 0x3f, 0x72, 0xb5, 0x2f, 0x38, 0x85, 0xb5, - 0x71, 0x94, 0x81, 0x31, 0xe5, 0x3b, 0x24, 0xe7, 0x95, 0x30, 0xb3, 0x22, 0x7f, 0x45, 0x40, 0xe1, - 0x90, 0xe5, 0x3b, 0x11, 0x7e, 0x54, 0xd9, 0xe3, 0xad, 0x9a, 0x77, 0x99, 0x2a, 0xa4, 0x24, 0xdb, - 0x6b, 0x69, 0x26, 0x48, 0x13, 0xcb, 0xf1, 0x05, 0x4d, 0xdb, 0x0f, 0x4a, 0xf5, 0xc0, 0x79, 0x40, - 0x07, 0xd8, 0x74, 0xa2, 0x17, 0x34, 0x6d, 0x3f, 0xb0, 0x6c, 0x24, 0x4b, 0xbc, 0xa0, 0x19, 0x32, - 0x34, 0x7e, 0x30, 0x03, 0x70, 0xaf, 0x5a, 0xc6, 0x6c, 0x99, 0x8f, 0xab, 0x14, 0xa4, 0x67, 0x20, - 0x93, 0xdc, 0xfb, 0xa8, 0x03, 0xff, 0x5d, 0x06, 0x26, 0x75, 0x34, 0xf2, 0x2e, 0x4c, 0xd5, 0xea, - 0x9e, 0xdb, 0x6c, 0x6e, 0xdb, 0xf5, 0xfd, 0x55, 0xa7, 0x4d, 0x79, 0xee, 0xa7, 0x21, 0xbe, 0x17, - 0xf9, 0x61, 0x91, 0xd5, 0x64, 0x65, 0x66, 0x1c, 0x99, 0xfc, 0x50, 0x06, 0x26, 0x6a, 0x7b, 0xee, - 0x41, 0xf4, 0xb0, 0x39, 0x9f, 0x90, 0xaf, 0xb1, 0x6f, 0xdb, 0xdf, 0x73, 0x0f, 0xac, 0x94, 0xd7, - 0xcd, 0x3f, 0x3e, 0x5a, 0x78, 0x67, 0xb0, 0xcb, 0xde, 0xba, 0x8b, 0xe7, 0x91, 0xc0, 0xbf, 0xa2, - 0x55, 0x62, 0xea, 0x75, 0x1a, 0x7f, 0x9a, 0x81, 0x31, 0x3c, 0xb9, 0x34, 0x9b, 0xa8, 0x73, 0x7d, - 0x96, 0xde, 0xb3, 0x09, 0xfb, 0xd5, 0x67, 0x62, 0xdf, 0x80, 0xa9, 0x18, 0x1a, 0x31, 0x60, 0xb8, - 0x86, 0xa1, 0xc5, 0xaa, 0x99, 0x81, 0x07, 0x1b, 0x9b, 0xa2, 0xc4, 0x58, 0x56, 0xc8, 0xee, 0x5f, - 0xc3, 0xbb, 0xc2, 0x45, 0x00, 0x47, 0x82, 0xe4, 0xc9, 0x86, 0xc4, 0x5b, 0x72, 0xff, 0x9a, 0xa9, - 0x60, 0x19, 0x6b, 0x30, 0x5c, 0x73, 0xbd, 0x60, 0xe9, 0x90, 0x1f, 0x26, 0x2a, 0xd4, 0xaf, 0xab, - 0x97, 0x81, 0x0e, 0x1a, 0xe0, 0xeb, 0xa6, 0x28, 0x22, 0x0b, 0x30, 0x74, 0xd3, 0xa1, 0xcd, 0x86, - 0xea, 0xf5, 0xb9, 0xc3, 0x00, 0x26, 0x87, 0xb3, 0x03, 0xd7, 0xe9, 0x28, 0xa9, 0x74, 0xe4, 0x5e, - 0xfa, 0xb8, 0xdf, 0x4d, 0x59, 0x1b, 0xdf, 0x17, 0xf4, 0xe7, 0x62, 0xb5, 0x9a, 0xfa, 0x0c, 0xf5, - 0xbf, 0x9f, 0x81, 0xf9, 0xde, 0x24, 0xaa, 0xc7, 0x6a, 0xa6, 0x8f, 0xc7, 0xea, 0x4b, 0xf1, 0xcb, - 0x2b, 0x44, 0x13, 0x97, 0x57, 0xd1, 0x95, 0x55, 0x05, 0x1d, 0x86, 0xeb, 0xe1, 0x6b, 0xde, 0xe7, - 0xfb, 0xb4, 0x19, 0x11, 0xf9, 0x34, 0x07, 0x48, 0x63, 0x0a, 0x5a, 0xe3, 0xd7, 0xf3, 0x70, 0xb6, - 0x27, 0x05, 0x59, 0x51, 0xf2, 0xd3, 0x4f, 0x86, 0x99, 0xb1, 0x7b, 0xe2, 0x5f, 0xc1, 0x7f, 0xd1, - 0x27, 0x2c, 0x1e, 0xc7, 0xb2, 0x1e, 0xe6, 0x25, 0xcf, 0x22, 0xaf, 0x57, 0x8f, 0xe5, 0xc5, 0xd1, - 0x91, 0x19, 0x24, 0x53, 0x94, 0x63, 0xc4, 0x13, 0x0d, 0x6c, 0xa7, 0xe9, 0xab, 0x9f, 0x5d, 0x83, - 0x83, 0x4c, 0x59, 0x16, 0xb9, 0x11, 0xe7, 0xd3, 0xdd, 0x88, 0x8d, 0xff, 0x27, 0x03, 0xa3, 0x61, - 0xb3, 0xc9, 0x3c, 0x9c, 0xde, 0x34, 0x4b, 0xe5, 0x65, 0x6b, 0xf3, 0x83, 0x8d, 0x65, 0xeb, 0xde, - 0x5a, 0x6d, 0x63, 0xb9, 0x5c, 0xbd, 0x59, 0x5d, 0xae, 0x14, 0x9f, 0x21, 0xd3, 0x30, 0x71, 0x6f, - 0xed, 0xce, 0xda, 0xfa, 0xd6, 0x9a, 0xb5, 0x6c, 0x9a, 0xeb, 0x66, 0x31, 0x43, 0x26, 0x60, 0xd4, - 0x5c, 0x2a, 0x95, 0xad, 0xb5, 0xf5, 0xca, 0x72, 0x31, 0x4b, 0x8a, 0x30, 0x5e, 0x5e, 0x5f, 0x5b, - 0x5b, 0x2e, 0x6f, 0x56, 0xef, 0x57, 0x37, 0x3f, 0x28, 0xe6, 0x08, 0x81, 0x49, 0x44, 0xd8, 0x30, - 0xab, 0x6b, 0xe5, 0xea, 0x46, 0x69, 0xb5, 0x98, 0x67, 0x30, 0x86, 0xaf, 0xc0, 0x86, 0x42, 0x46, - 0x77, 0xee, 0x2d, 0x2d, 0x17, 0x87, 0x19, 0x0a, 0xfb, 0x4b, 0x41, 0x19, 0x61, 0xd5, 0x23, 0x4a, - 0xa5, 0xb4, 0x59, 0x5a, 0x2a, 0xd5, 0x96, 0x8b, 0x05, 0x72, 0x06, 0x66, 0x34, 0x90, 0xb5, 0xba, - 0x7e, 0xab, 0xba, 0x56, 0x1c, 0x25, 0xb3, 0x50, 0x0c, 0x61, 0x95, 0x25, 0xeb, 0x5e, 0x6d, 0xd9, - 0x2c, 0x42, 0x1c, 0xba, 0x56, 0xba, 0xbb, 0x5c, 0x1c, 0x33, 0xde, 0xe1, 0x11, 0x46, 0x7c, 0xa8, - 0xc9, 0x69, 0x20, 0xb5, 0xcd, 0xd2, 0xe6, 0xbd, 0x5a, 0xac, 0xf3, 0x63, 0x30, 0x52, 0xbb, 0x57, - 0x2e, 0x2f, 0xd7, 0x6a, 0xc5, 0x0c, 0x01, 0x18, 0xbe, 0x59, 0xaa, 0xae, 0x2e, 0x57, 0x8a, 0x59, - 0xe3, 0x27, 0x32, 0x30, 0x2d, 0x35, 0x40, 0x79, 0x13, 0xf1, 0x98, 0xdf, 0xe2, 0xbb, 0xda, 0xc1, - 0x56, 0x06, 0x80, 0xc4, 0x2a, 0xe9, 0xf3, 0x19, 0x7a, 0x70, 0x2a, 0x15, 0x99, 0x7c, 0x00, 0x45, - 0xd9, 0x80, 0xbb, 0x76, 0x50, 0xdf, 0x8b, 0xc4, 0xd8, 0xf3, 0xb1, 0x4a, 0x62, 0x68, 0xdc, 0x36, - 0x19, 0x3d, 0x98, 0x97, 0x60, 0x63, 0xfc, 0x6c, 0x06, 0xce, 0xf4, 0x20, 0x26, 0x65, 0x18, 0x0e, - 0xd3, 0x75, 0xf7, 0xf1, 0x75, 0x9a, 0xfd, 0xee, 0xd1, 0x82, 0x40, 0xc4, 0x77, 0xc3, 0xf0, 0x2f, - 0x73, 0x38, 0xcc, 0xbf, 0x8d, 0x49, 0xb0, 0xf9, 0x98, 0x9c, 0x8d, 0x0d, 0xa7, 0xa8, 0xa9, 0xb4, - 0x55, 0x5b, 0x1a, 0x13, 0x03, 0x92, 0xb3, 0x0f, 0x7c, 0xcc, 0x82, 0x6d, 0xfc, 0x54, 0x86, 0x69, - 0x6c, 0x71, 0x44, 0xa6, 0xc8, 0x96, 0x7c, 0xbf, 0xdb, 0xa2, 0xa6, 0xdb, 0xa4, 0x25, 0x73, 0x4d, - 0xec, 0x05, 0xa8, 0x82, 0xda, 0x58, 0x80, 0x67, 0x05, 0xcb, 0xf6, 0xda, 0xda, 0xbd, 0xa6, 0x4a, - 0x43, 0xde, 0x04, 0x08, 0xdf, 0x6f, 0x97, 0x91, 0xfd, 0x3c, 0xb3, 0x85, 0x80, 0xea, 0x4a, 0xb4, - 0x82, 0x6c, 0xfc, 0x95, 0x0c, 0x8c, 0x8b, 0x93, 0x50, 0xa9, 0x49, 0xbd, 0xe0, 0xf1, 0xd6, 0xcc, - 0x9b, 0xda, 0x9a, 0x09, 0x5d, 0xfb, 0x15, 0xfe, 0xac, 0x38, 0x75, 0xb9, 0xfc, 0xe3, 0x0c, 0x14, - 0xe3, 0x88, 0xe4, 0x5d, 0x28, 0xd4, 0xe8, 0x03, 0xea, 0x39, 0xc1, 0xa1, 0x90, 0x7e, 0xf2, 0x61, - 0x13, 0x8e, 0x23, 0xca, 0xb8, 0xad, 0xd6, 0x17, 0xbf, 0xcc, 0x90, 0x66, 0x50, 0x21, 0xae, 0xd8, - 0x32, 0x72, 0x4f, 0xca, 0x96, 0x61, 0xfc, 0xcf, 0x59, 0x38, 0x73, 0x8b, 0x06, 0x6a, 0x9f, 0xc2, - 0x8b, 0xe8, 0xcf, 0x0d, 0xd6, 0x2f, 0xa5, 0x27, 0x73, 0x30, 0x82, 0x45, 0x72, 0x7e, 0x4d, 0xf9, - 0x93, 0x2c, 0x85, 0xeb, 0x3a, 0xa7, 0xbd, 0x9c, 0xd0, 0xa3, 0xee, 0x2b, 0x4a, 0x2e, 0xf5, 0x70, - 0x59, 0x5f, 0x84, 0x49, 0x4c, 0x16, 0xda, 0x65, 0x9f, 0x03, 0x6d, 0x08, 0x9b, 0x4e, 0xc1, 0x8c, - 0x41, 0xc9, 0x2b, 0x50, 0x64, 0x90, 0x52, 0x7d, 0xbf, 0xed, 0x1e, 0x34, 0x69, 0x63, 0x97, 0xf2, - 0x07, 0xb7, 0x0b, 0x66, 0x02, 0x2e, 0x79, 0xde, 0x6b, 0xf3, 0xf3, 0x18, 0x6d, 0xa0, 0xe1, 0x45, - 0xf0, 0x8c, 0xa0, 0xf3, 0x6f, 0xc2, 0xd8, 0x27, 0x7c, 0x17, 0xc1, 0xf8, 0x9f, 0x32, 0x30, 0x8b, - 0x9d, 0x53, 0x2a, 0x96, 0x6f, 0x56, 0xc9, 0xd1, 0x52, 0x52, 0x85, 0xdb, 0x0c, 0xa4, 0x7f, 0x0a, - 0xe1, 0x28, 0x46, 0x86, 0x9e, 0xec, 0x00, 0x86, 0x9e, 0xda, 0x49, 0xde, 0xe7, 0x1c, 0xd0, 0x4e, - 0xc5, 0x5f, 0x55, 0x8f, 0xa6, 0xdc, 0xf8, 0xa1, 0x2c, 0x8c, 0x98, 0x14, 0x1f, 0x2e, 0x24, 0x17, - 0x61, 0x64, 0xcd, 0x0d, 0xa8, 0x7f, 0x57, 0x7b, 0xa5, 0xb2, 0xcd, 0x40, 0x56, 0xab, 0x61, 0xca, - 0x42, 0xb6, 0xe0, 0x37, 0x3c, 0xb7, 0xd1, 0xad, 0x07, 0xea, 0x82, 0xef, 0x70, 0x90, 0x29, 0xcb, - 0xc8, 0x6b, 0x30, 0x2a, 0x38, 0x87, 0xd7, 0x7f, 0xe8, 0xb6, 0xea, 0xd1, 0xf0, 0xe1, 0xcb, 0x08, - 0x01, 0x15, 0x55, 0xae, 0x35, 0xe4, 0x15, 0x45, 0x35, 0xa1, 0x08, 0x48, 0xfd, 0x7b, 0xa8, 0x8f, - 0xfe, 0xfd, 0x39, 0x18, 0x2e, 0xf9, 0x3e, 0x0d, 0x64, 0xd0, 0xf3, 0x78, 0x98, 0x36, 0xc6, 0xa7, - 0x01, 0x67, 0x6c, 0x63, 0xb9, 0x29, 0xf0, 0x8c, 0x7f, 0x91, 0x85, 0x21, 0xfc, 0x13, 0xaf, 0x3c, - 0xbd, 0xfa, 0x9e, 0x76, 0xe5, 0xe9, 0xd5, 0xf7, 0x4c, 0x84, 0x92, 0x6b, 0x68, 0x7e, 0x90, 0x59, - 0xed, 0x45, 0xef, 0xd1, 0xae, 0xde, 0x88, 0xc0, 0xa6, 0x8a, 0x13, 0xde, 0x05, 0xe7, 0x52, 0x53, - 0x1d, 0x9c, 0x86, 0xec, 0x7a, 0x4d, 0xf4, 0x18, 0xf3, 0xa8, 0xb8, 0xbe, 0x99, 0x5d, 0xaf, 0xe1, - 0x68, 0xac, 0x94, 0x16, 0xdf, 0xb8, 0xa1, 0x3e, 0xa8, 0xea, 0xef, 0xd9, 0x8b, 0x6f, 0xdc, 0x30, - 0x45, 0x09, 0x1b, 0x5f, 0x6c, 0x33, 0xde, 0x89, 0xf2, 0x20, 0x5d, 0x1c, 0x5f, 0xec, 0x1b, 0xde, - 0x7f, 0x9a, 0x11, 0x02, 0x59, 0x84, 0x31, 0x11, 0x1a, 0x8e, 0xf8, 0x4a, 0xe8, 0xb6, 0x08, 0x1d, - 0xe7, 0x14, 0x2a, 0x12, 0xbf, 0x1d, 0x13, 0x13, 0x24, 0xdf, 0xde, 0x12, 0xb7, 0x63, 0x72, 0x0a, - 0x7d, 0x53, 0x41, 0x89, 0x62, 0x8c, 0xa3, 0xe0, 0x5b, 0x35, 0xc6, 0x18, 0x93, 0xbf, 0x86, 0x08, - 0xc6, 0x2f, 0x66, 0xa1, 0xb0, 0xd1, 0xec, 0xee, 0x3a, 0xed, 0xfb, 0xd7, 0xfe, 0x4c, 0x1f, 0xf5, - 0x7f, 0x1d, 0x70, 0x93, 0x10, 0x27, 0x02, 0x69, 0xa5, 0xe6, 0x4d, 0x13, 0xca, 0x07, 0x27, 0x41, - 0x34, 0x72, 0x1d, 0xc4, 0xc2, 0x14, 0x6f, 0x3c, 0x9e, 0xd2, 0x09, 0xf8, 0xab, 0x39, 0x92, 0x44, - 0xa0, 0x92, 0xb7, 0x61, 0x2c, 0x7a, 0x5d, 0x3d, 0x7a, 0xba, 0x51, 0xa5, 0x2c, 0x47, 0xe5, 0xf7, - 0xaf, 0x99, 0x2a, 0xba, 0xf1, 0x9f, 0x0d, 0xc3, 0xb8, 0xda, 0x1e, 0x62, 0xc2, 0x8c, 0xdf, 0x64, - 0x07, 0x72, 0xe1, 0x96, 0xd4, 0xc1, 0x42, 0xb1, 0x9d, 0x9e, 0xd7, 0x1b, 0xc4, 0xf0, 0xb8, 0x8f, - 0x92, 0x8c, 0x69, 0x5f, 0x79, 0xc6, 0x9c, 0xf6, 0x23, 0x30, 0xc7, 0x23, 0x25, 0x28, 0xb8, 0x1d, - 0x7f, 0x97, 0xb6, 0x1d, 0x79, 0x89, 0x72, 0x41, 0x63, 0xb4, 0x2e, 0x0a, 0x13, 0xbc, 0x42, 0x32, - 0xf2, 0x06, 0x0c, 0xbb, 0x1d, 0xda, 0xb6, 0x1d, 0xb1, 0xc7, 0x3d, 0x1b, 0x63, 0x40, 0xdb, 0xa5, - 0xaa, 0x42, 0x28, 0x90, 0xc9, 0x55, 0xc8, 0xbb, 0xfb, 0xe1, 0x7c, 0x9d, 0xd5, 0x89, 0xf6, 0x03, - 0x5b, 0x21, 0x41, 0x44, 0x46, 0xf0, 0x91, 0xdd, 0xda, 0x11, 0x33, 0xa6, 0x13, 0xdc, 0xb6, 0x5b, - 0x3b, 0x2a, 0x01, 0x43, 0x24, 0xef, 0x01, 0x74, 0xec, 0x5d, 0xea, 0x59, 0x8d, 0x6e, 0x70, 0x28, - 0xe6, 0xed, 0x79, 0x8d, 0x6c, 0x83, 0x15, 0x57, 0xba, 0xc1, 0xa1, 0x42, 0x3b, 0xda, 0x91, 0x40, - 0x52, 0x02, 0x68, 0xd9, 0x41, 0x40, 0xbd, 0x96, 0x2b, 0xfc, 0xc2, 0xc6, 0xc2, 0xa7, 0x11, 0x39, - 0x83, 0xbb, 0x61, 0xb1, 0xc2, 0x41, 0x21, 0xc2, 0x46, 0x3b, 0x9e, 0x2d, 0x5e, 0xda, 0x8c, 0x35, - 0xda, 0xf1, 0xb4, 0x5e, 0x32, 0x44, 0xf2, 0x45, 0x18, 0x69, 0x38, 0x7e, 0xdd, 0xf5, 0x1a, 0x22, - 0xd9, 0xc1, 0x73, 0x1a, 0x4d, 0x85, 0x97, 0x29, 0x64, 0x12, 0x9d, 0xb5, 0x56, 0x24, 0x41, 0x5b, - 0x73, 0x0f, 0xd0, 0x76, 0x1f, 0x6f, 0x6d, 0x2d, 0x2c, 0x56, 0x5b, 0x1b, 0x11, 0xb1, 0xa9, 0xdc, - 0x75, 0x82, 0xa6, 0xbd, 0x2d, 0xae, 0xa0, 0xf5, 0xa9, 0xbc, 0x85, 0x45, 0xea, 0x54, 0x72, 0x64, - 0xf2, 0x26, 0x14, 0x68, 0x3b, 0xf0, 0x6c, 0xcb, 0x69, 0x88, 0x78, 0x3a, 0xbd, 0xd1, 0x6c, 0x03, - 0xb6, 0xab, 0x15, 0xb5, 0xd1, 0x88, 0x5f, 0x6d, 0xb0, 0xf1, 0xf1, 0xeb, 0x4e, 0x4b, 0x84, 0xc1, - 0xe9, 0xe3, 0x53, 0x2b, 0x57, 0xef, 0xaa, 0xe3, 0xc3, 0x10, 0xc9, 0xf3, 0x00, 0xbb, 0xb4, 0x4d, - 0x79, 0x4c, 0x2a, 0xbf, 0x65, 0x30, 0x15, 0xc8, 0x97, 0xf2, 0xff, 0xeb, 0xcf, 0x2f, 0x64, 0x96, - 0x00, 0x0a, 0x32, 0xdb, 0x83, 0xb1, 0x0a, 0x67, 0x7b, 0x7e, 0x14, 0xe4, 0x32, 0x14, 0x77, 0x6c, - 0x61, 0xe7, 0xaa, 0xef, 0xd9, 0xed, 0x36, 0x6d, 0x0a, 0x71, 0x34, 0x25, 0xe1, 0x65, 0x0e, 0xe6, - 0x9c, 0x8d, 0xf7, 0x60, 0x36, 0x6d, 0x34, 0xc8, 0x0b, 0x30, 0xae, 0x26, 0xb6, 0x10, 0x4c, 0xc6, - 0xec, 0x8e, 0x23, 0x53, 0x5b, 0x08, 0x06, 0xbf, 0x96, 0x81, 0xe7, 0xfa, 0x7d, 0x5b, 0x64, 0x1e, - 0x0a, 0x1d, 0xcf, 0x71, 0x51, 0x87, 0xe3, 0x12, 0x30, 0xfc, 0x4d, 0xce, 0x01, 0x70, 0x65, 0x23, - 0xb0, 0x77, 0x85, 0x9f, 0xbc, 0x39, 0x8a, 0x90, 0x4d, 0x7b, 0xd7, 0x27, 0xaf, 0xc2, 0x74, 0x83, - 0xee, 0xd8, 0xdd, 0x66, 0x60, 0xf9, 0xf5, 0x3d, 0xda, 0xc0, 0xd0, 0x14, 0xf4, 0x7f, 0x32, 0x8b, - 0xa2, 0xa0, 0x26, 0xe1, 0x89, 0x16, 0x0f, 0xf5, 0x68, 0xf1, 0xed, 0x7c, 0x21, 0x53, 0xcc, 0x9a, - 0xe8, 0x06, 0x64, 0x7c, 0x3b, 0x0b, 0x73, 0xbd, 0x16, 0x13, 0x79, 0x27, 0x6d, 0x0c, 0xb8, 0xa9, - 0x5e, 0x85, 0xab, 0xa6, 0x7a, 0xa5, 0x36, 0xb2, 0x08, 0x61, 0x60, 0xc9, 0x71, 0x41, 0xe2, 0x12, - 0xc6, 0x68, 0x3a, 0xb6, 0xef, 0x1f, 0xb0, 0xef, 0x25, 0xa7, 0x64, 0x9b, 0x13, 0x30, 0x95, 0x46, - 0xc2, 0xc8, 0x17, 0x00, 0xea, 0x4d, 0xd7, 0xa7, 0x78, 0x23, 0x2e, 0x36, 0x62, 0xee, 0x5d, 0x1b, - 0x42, 0xd5, 0x2b, 0x50, 0x84, 0x96, 0xdd, 0x06, 0x15, 0x13, 0x68, 0xc3, 0x99, 0x1e, 0xd2, 0x83, - 0x4d, 0x4f, 0xf4, 0xa0, 0xa4, 0x4c, 0x4f, 0xdf, 0x0d, 0x9f, 0x95, 0x8c, 0x8f, 0x78, 0xb6, 0xd7, - 0x1a, 0x39, 0x04, 0x92, 0x14, 0x11, 0x8c, 0xbb, 0xf0, 0x11, 0xed, 0x7a, 0x21, 0x77, 0x0e, 0xb9, - 0xe7, 0x35, 0xc9, 0x02, 0x8c, 0xc9, 0xe7, 0x67, 0x98, 0xa2, 0xcb, 0x99, 0x83, 0x00, 0xdd, 0xa1, - 0xb8, 0x78, 0x30, 0x29, 0x22, 0x86, 0x0f, 0x89, 0x2d, 0x74, 0x14, 0x21, 0x9b, 0x87, 0x1d, 0xd9, - 0xbb, 0xe7, 0xe4, 0xfa, 0xd6, 0x05, 0xb7, 0x28, 0xfd, 0xe9, 0x8c, 0x9c, 0xfe, 0xa4, 0xe4, 0x3b, - 0xae, 0x7d, 0x04, 0x30, 0xd8, 0x43, 0x34, 0x0c, 0xff, 0x66, 0x5b, 0xba, 0xfc, 0xea, 0xc4, 0x96, - 0x2e, 0x7e, 0x92, 0x8b, 0x30, 0xe5, 0x71, 0x77, 0xc0, 0xc0, 0x15, 0xe3, 0x89, 0x33, 0x65, 0x4e, - 0x70, 0xf0, 0xa6, 0x8b, 0x63, 0x2a, 0xda, 0x75, 0x3b, 0x1c, 0x30, 0x65, 0x23, 0x20, 0x57, 0x60, - 0x94, 0x6d, 0x04, 0x98, 0x37, 0x22, 0xe6, 0x65, 0x8e, 0x78, 0xb8, 0xad, 0x9a, 0x85, 0x8f, 0xc4, - 0xdf, 0x82, 0xd7, 0x7f, 0x91, 0x91, 0xcc, 0xd4, 0x6d, 0x88, 0x9c, 0x81, 0x11, 0xd7, 0xdb, 0x55, - 0xba, 0x36, 0xec, 0x7a, 0xbb, 0xac, 0x5f, 0x97, 0xa0, 0xc8, 0x83, 0x1e, 0x78, 0x34, 0xb9, 0x7f, - 0xd8, 0xe6, 0xe7, 0xd4, 0x82, 0x39, 0xc9, 0xe1, 0xf8, 0xc6, 0xe6, 0x61, 0xbb, 0xce, 0x30, 0x7d, - 0xdf, 0xb5, 0xd4, 0x64, 0x31, 0xa2, 0xdb, 0x93, 0xbe, 0xef, 0x46, 0x59, 0x63, 0x1a, 0x64, 0x09, - 0x26, 0x18, 0x9f, 0x30, 0x65, 0x8d, 0xd8, 0x25, 0xcf, 0x25, 0x77, 0xc9, 0xc3, 0x76, 0x5d, 0x36, - 0xd1, 0x1c, 0xf7, 0x95, 0x5f, 0xa2, 0x37, 0x3f, 0x93, 0x85, 0xd3, 0xe9, 0xe8, 0x38, 0x5f, 0xac, - 0x12, 0x8c, 0xfd, 0xe1, 0x36, 0x4b, 0x73, 0x94, 0x41, 0x78, 0x7a, 0x83, 0xb4, 0xd6, 0x66, 0x53, - 0x5b, 0xfb, 0x0a, 0x4c, 0x23, 0x23, 0xa1, 0x97, 0x34, 0x1d, 0x3f, 0x10, 0x51, 0xfb, 0xe6, 0x14, - 0x2b, 0xe0, 0x02, 0x6e, 0x95, 0x81, 0xc9, 0x4b, 0x30, 0x29, 0x45, 0x94, 0x7b, 0xd0, 0x66, 0x15, - 0x73, 0xf9, 0x34, 0x21, 0xa0, 0xeb, 0x08, 0x24, 0xa7, 0x60, 0xd8, 0xee, 0x74, 0x58, 0x95, 0x5c, - 0x2c, 0x0d, 0xd9, 0x9d, 0x4e, 0xb5, 0x41, 0x2e, 0xc0, 0x04, 0x46, 0x3a, 0x59, 0x3b, 0xe8, 0x28, - 0x22, 0x7c, 0xcb, 0xcc, 0x71, 0x04, 0x72, 0xe7, 0x11, 0x9f, 0x7d, 0x08, 0x8c, 0x56, 0xa2, 0x8c, - 0x20, 0x0a, 0xd8, 0x1d, 0x89, 0x20, 0x46, 0xe6, 0x8b, 0x30, 0x25, 0x76, 0x53, 0x21, 0xe1, 0x91, - 0x52, 0xac, 0x3f, 0xa6, 0xe6, 0x8a, 0xbc, 0xdc, 0x20, 0x40, 0xd5, 0x86, 0xa4, 0xfc, 0xfd, 0x0c, - 0x9c, 0x4a, 0xdd, 0x8e, 0xc9, 0x37, 0x80, 0x07, 0x7e, 0x04, 0xae, 0xe5, 0xd1, 0xba, 0xd3, 0x71, - 0x30, 0x34, 0x9e, 0x1b, 0xa1, 0x16, 0xfb, 0x6d, 0xe4, 0x18, 0x44, 0xb2, 0xe9, 0x9a, 0x21, 0x11, - 0x3f, 0x47, 0x17, 0xbd, 0x18, 0x78, 0xfe, 0x43, 0x38, 0x95, 0x8a, 0x9a, 0x72, 0xbe, 0x7d, 0x4d, - 0x7f, 0xbe, 0x4c, 0xde, 0x2a, 0xc4, 0x3a, 0xad, 0x9c, 0x7b, 0x45, 0xf7, 0x7e, 0x33, 0xec, 0x5e, - 0x6c, 0xe3, 0x26, 0xcb, 0xf1, 0x65, 0x99, 0xa6, 0x7b, 0x4a, 0xa2, 0x9e, 0x2b, 0x93, 0x7c, 0x08, - 0xa7, 0xc4, 0x52, 0xd9, 0xf5, 0xec, 0xce, 0x5e, 0xc4, 0x8e, 0x37, 0xf4, 0xe5, 0x34, 0x76, 0x7c, - 0x0d, 0xdd, 0x62, 0xf8, 0x21, 0xd7, 0x19, 0x3b, 0x09, 0x14, 0x7d, 0xf0, 0xe4, 0xa6, 0x9f, 0xd2, - 0x9a, 0x94, 0x35, 0x98, 0x49, 0x5b, 0x83, 0x03, 0x7f, 0x00, 0xa2, 0xce, 0x1f, 0xc8, 0xc0, 0xf9, - 0xe3, 0xda, 0x4c, 0xb6, 0xe0, 0x34, 0xde, 0x7b, 0xfb, 0x6e, 0xd8, 0x6d, 0xab, 0x6e, 0xd7, 0xf7, - 0xa8, 0x58, 0x25, 0x46, 0x6a, 0xe7, 0x3b, 0x9d, 0x5a, 0x6d, 0x5d, 0xe9, 0x77, 0xa7, 0x53, 0xf3, - 0x5d, 0xf9, 0xbb, 0xcc, 0xc8, 0x45, 0x1b, 0x1a, 0xf0, 0x6c, 0x1f, 0x4a, 0xe5, 0xb3, 0xca, 0xa8, - 0x9f, 0xd5, 0x25, 0x28, 0xee, 0xd0, 0x06, 0x53, 0xa1, 0x68, 0x03, 0x9b, 0xf6, 0x60, 0x91, 0xbf, - 0x01, 0x68, 0x4e, 0x86, 0xf0, 0x9a, 0xef, 0xde, 0x5f, 0x14, 0xb5, 0xb4, 0xa4, 0x84, 0x54, 0x55, - 0x34, 0x72, 0x05, 0x66, 0x62, 0x61, 0xfe, 0x51, 0xdc, 0xa8, 0x39, 0xcd, 0x8a, 0xf4, 0xa4, 0x30, - 0x2f, 0xc0, 0xb8, 0x9c, 0x06, 0x2f, 0x8c, 0x3e, 0x31, 0xc7, 0x04, 0x8c, 0xad, 0x72, 0x51, 0xdd, - 0xdf, 0xcb, 0x4a, 0x95, 0x69, 0xc9, 0x75, 0x03, 0x3f, 0xf0, 0xec, 0x8e, 0x76, 0x6e, 0x22, 0x2d, - 0x38, 0xeb, 0xda, 0xdd, 0x60, 0x6f, 0xd1, 0x62, 0xff, 0xba, 0x9e, 0x0c, 0x05, 0xad, 0x4b, 0x4f, - 0xb8, 0xb1, 0xc5, 0xab, 0xba, 0xe8, 0x2c, 0x31, 0xec, 0x92, 0x8a, 0xcc, 0x76, 0x78, 0x85, 0xeb, - 0xca, 0x33, 0xe6, 0x19, 0xce, 0x33, 0x81, 0x45, 0x56, 0x60, 0x5c, 0x7d, 0xbe, 0x3f, 0xf5, 0xe0, - 0xa4, 0xbc, 0xe2, 0xaf, 0x73, 0x1d, 0xdb, 0x8e, 0x4a, 0xc8, 0xbb, 0x30, 0xea, 0x34, 0x44, 0xea, - 0x39, 0x71, 0x7c, 0xd2, 0x55, 0xf6, 0x6a, 0x83, 0x67, 0xa2, 0x8b, 0x78, 0xb0, 0xb3, 0x97, 0x23, - 0xa0, 0x4b, 0x13, 0xda, 0x09, 0xd3, 0x58, 0x92, 0xbb, 0x73, 0x92, 0x2c, 0xf1, 0x54, 0xfa, 0x69, - 0x18, 0xf6, 0x95, 0x5c, 0x78, 0xa6, 0xf8, 0x65, 0x7c, 0x1f, 0x5c, 0x1a, 0x74, 0x8c, 0xc8, 0xeb, - 0x40, 0x7a, 0x0c, 0xf8, 0xa8, 0x39, 0x6d, 0x27, 0xc6, 0xed, 0x05, 0x50, 0x93, 0x79, 0x39, 0x72, - 0xc2, 0x25, 0xec, 0x9e, 0xe7, 0x18, 0x3f, 0x98, 0x83, 0x49, 0xfd, 0x4c, 0x4d, 0x5e, 0x85, 0x7c, - 0xc8, 0x76, 0x32, 0xb4, 0xfd, 0xaa, 0x48, 0x8c, 0xb9, 0x89, 0x48, 0x6c, 0x83, 0xc0, 0xfb, 0x1f, - 0xab, 0xa5, 0x9a, 0x67, 0xcd, 0x71, 0x04, 0x4a, 0xb3, 0xec, 0x6d, 0xe0, 0x4f, 0xce, 0xa2, 0x2c, - 0x0b, 0x06, 0x7b, 0x60, 0xbd, 0xc0, 0x4e, 0xf6, 0x68, 0x57, 0x1b, 0x67, 0xb4, 0x4c, 0x9e, 0xe0, - 0x9b, 0xea, 0xd1, 0x91, 0x29, 0xdf, 0xfb, 0xc8, 0x24, 0xba, 0xd2, 0xe3, 0xc8, 0x34, 0xd4, 0xe7, - 0xc8, 0x14, 0x51, 0xaa, 0x47, 0x26, 0x3c, 0x38, 0x8f, 0xf4, 0x3a, 0x38, 0x47, 0x34, 0xfc, 0xe0, - 0xfc, 0xa2, 0xe8, 0xae, 0x67, 0x1f, 0x58, 0x38, 0x0e, 0xdc, 0x53, 0x8d, 0x77, 0xc4, 0xb4, 0x0f, - 0xf0, 0xa6, 0x6c, 0x69, 0x14, 0xe4, 0xf5, 0x9a, 0xf1, 0xd7, 0x33, 0xb1, 0x43, 0x8e, 0x9c, 0x8a, - 0x97, 0x60, 0xd2, 0x69, 0x31, 0xed, 0x8b, 0x36, 0x14, 0xad, 0x61, 0xc2, 0x9c, 0x90, 0x50, 0xae, - 0x39, 0xbc, 0x0c, 0x53, 0x21, 0x1a, 0x0f, 0x4c, 0xe6, 0xce, 0xf3, 0x66, 0x48, 0x2d, 0x02, 0x93, - 0x5f, 0x85, 0xe9, 0x10, 0x51, 0x28, 0xaa, 0x5c, 0x71, 0x98, 0x30, 0x8b, 0xb2, 0x40, 0x3c, 0x9e, - 0xe8, 0x1b, 0xbb, 0xf1, 0x5d, 0xe9, 0x53, 0x6a, 0x95, 0xf1, 0x9b, 0x39, 0x4d, 0x01, 0x94, 0xd5, - 0x2c, 0xc1, 0x18, 0x13, 0x8d, 0x62, 0x90, 0x84, 0x58, 0x79, 0xa1, 0xc7, 0xf0, 0x8b, 0x0b, 0xca, - 0x5a, 0x6d, 0xdd, 0x04, 0xdf, 0x77, 0xe5, 0x7d, 0xa5, 0xc5, 0xa5, 0x3f, 0xd7, 0x61, 0x70, 0xf9, - 0x49, 0x76, 0x5c, 0x86, 0xbc, 0xd2, 0x9f, 0x5d, 0xa9, 0xd3, 0xc1, 0x36, 0xb2, 0xd5, 0x87, 0xbb, - 0x40, 0xf8, 0x4b, 0x56, 0x70, 0x0f, 0xf0, 0xbc, 0xe4, 0xeb, 0xcc, 0x73, 0x29, 0xfb, 0x6a, 0x82, - 0x39, 0x8e, 0x12, 0x72, 0xc6, 0x27, 0xee, 0x7d, 0x95, 0xed, 0x32, 0x8c, 0xb3, 0xd3, 0x77, 0xc8, - 0x30, 0xaf, 0x45, 0x11, 0xf5, 0xea, 0x7c, 0xb9, 0x7a, 0xd7, 0x1c, 0x63, 0x74, 0x92, 0xcd, 0x1e, - 0x9c, 0x55, 0x75, 0x44, 0xbd, 0x91, 0x43, 0x32, 0x4b, 0x5b, 0xdf, 0x11, 0x88, 0x54, 0x49, 0x6c, - 0xea, 0x69, 0x5b, 0x07, 0x08, 0x34, 0x63, 0x0f, 0xe6, 0x7b, 0x4f, 0x09, 0x3b, 0x76, 0x50, 0x35, - 0x74, 0xd9, 0x94, 0x3f, 0x95, 0x0d, 0x32, 0xab, 0x6e, 0x90, 0x67, 0xa1, 0x20, 0x9d, 0xdc, 0xe4, - 0x41, 0xc5, 0xe6, 0x0e, 0x6e, 0xc6, 0xdf, 0xc9, 0xc1, 0x85, 0x01, 0xa6, 0xab, 0x4f, 0x9d, 0xef, - 0xc3, 0x18, 0x37, 0x18, 0x72, 0xf1, 0xc9, 0x6f, 0xd0, 0xe5, 0x36, 0xc0, 0x98, 0x0a, 0x59, 0xc7, - 0x54, 0x98, 0x48, 0xde, 0x81, 0x1f, 0xfe, 0x4d, 0xbe, 0x01, 0x53, 0x5c, 0xa0, 0x71, 0x1f, 0x83, - 0x9d, 0x6e, 0x73, 0x00, 0x89, 0xf6, 0xac, 0x74, 0x88, 0x8e, 0x91, 0xa2, 0x90, 0x43, 0x89, 0x51, - 0x0b, 0x61, 0x64, 0x13, 0xc6, 0x10, 0x6d, 0xc7, 0x76, 0x9a, 0x03, 0x79, 0xe6, 0x4a, 0x77, 0x6b, - 0x95, 0x8c, 0xbb, 0x46, 0x31, 0xc0, 0x4d, 0xfc, 0xcd, 0x0e, 0x79, 0xed, 0x6e, 0xcb, 0xb2, 0x3b, - 0x1d, 0xbe, 0x16, 0xc4, 0xad, 0xcf, 0x90, 0x39, 0xd1, 0xee, 0xb6, 0x4a, 0x9d, 0x0e, 0x4e, 0x29, - 0x5e, 0x0f, 0x4d, 0x33, 0x3c, 0xfe, 0xd5, 0x4a, 0xcc, 0x61, 0xc4, 0x64, 0x0c, 0xf8, 0x77, 0x2b, - 0x70, 0x67, 0x81, 0x7b, 0x00, 0x70, 0x73, 0xb7, 0xc9, 0x7f, 0x18, 0x7f, 0x92, 0x95, 0xba, 0x59, - 0xef, 0x75, 0xff, 0x17, 0x53, 0x94, 0x32, 0x45, 0x97, 0xa0, 0xc8, 0x86, 0x3e, 0x12, 0x2a, 0xe1, - 0x1c, 0x4d, 0xb6, 0xbb, 0xad, 0x70, 0xec, 0xd4, 0x81, 0x1f, 0x56, 0x07, 0xfe, 0x0b, 0x52, 0x21, - 0x4d, 0x15, 0x0f, 0xbd, 0x87, 0xdc, 0xf8, 0x67, 0x39, 0xb8, 0x38, 0x98, 0x10, 0xf8, 0x8b, 0x79, - 0x4b, 0x99, 0xb7, 0xd8, 0x21, 0x78, 0x28, 0x7e, 0x08, 0x4e, 0xfb, 0xf6, 0x86, 0xd3, 0xbe, 0xbd, - 0xc4, 0x91, 0x7b, 0x24, 0xe5, 0xc8, 0x9d, 0xfa, 0x81, 0x16, 0x8e, 0xf9, 0x40, 0x47, 0xd5, 0x75, - 0xf2, 0x4f, 0xb3, 0x30, 0x93, 0x72, 0x25, 0x42, 0x3e, 0x84, 0x19, 0xa9, 0xda, 0xf3, 0x9d, 0x83, - 0xab, 0xdc, 0x7c, 0xf7, 0xbd, 0x9c, 0xa6, 0xd4, 0x23, 0x5a, 0x8a, 0xe2, 0x3d, 0x2d, 0xd4, 0xf9, - 0xa8, 0xfc, 0xcf, 0x8f, 0x22, 0x4f, 0x3e, 0x80, 0xd3, 0x98, 0x46, 0xb4, 0x6e, 0x29, 0xfa, 0xbc, - 0xe5, 0xd1, 0x1d, 0xb1, 0x1e, 0x5e, 0x48, 0xa8, 0xbd, 0x4e, 0x5d, 0x69, 0x8e, 0x49, 0x77, 0x56, - 0x9e, 0x31, 0x67, 0xfd, 0x14, 0x78, 0xfc, 0x8c, 0xf0, 0xef, 0x65, 0xc0, 0x38, 0x7e, 0xbc, 0xd0, - 0x56, 0x19, 0x1f, 0xf0, 0x51, 0x73, 0xcc, 0x56, 0x46, 0xef, 0x02, 0x4c, 0x78, 0x74, 0xc7, 0xa3, - 0xfe, 0x9e, 0x32, 0x7c, 0xa3, 0xe6, 0xb8, 0x00, 0xca, 0x81, 0x91, 0x79, 0x88, 0x4e, 0xa4, 0x64, - 0x4b, 0x22, 0xe3, 0x66, 0x78, 0xf4, 0x4b, 0x9d, 0x07, 0xb6, 0x9a, 0xd4, 0x06, 0xf2, 0x1f, 0xb7, - 0xf3, 0x85, 0x6c, 0x31, 0x67, 0x8a, 0x6c, 0x49, 0x3b, 0x4e, 0x93, 0x1a, 0xbf, 0x9a, 0x91, 0x1a, - 0x41, 0xda, 0xe0, 0x91, 0x0f, 0x15, 0x27, 0x9e, 0x5c, 0x42, 0x0d, 0x49, 0x23, 0x51, 0xfd, 0x1d, - 0x44, 0x02, 0x9f, 0xf8, 0x1b, 0xfb, 0x82, 0xe5, 0xe3, 0x78, 0x22, 0xbc, 0x29, 0xef, 0x00, 0x99, - 0xb4, 0xbb, 0x7f, 0x8d, 0x5c, 0x86, 0x11, 0x7e, 0xed, 0x27, 0x1b, 0x3a, 0xa5, 0x35, 0xf4, 0xfe, - 0x35, 0x53, 0x96, 0x1b, 0x3f, 0x9b, 0x09, 0x2f, 0x3e, 0xe2, 0xcd, 0xbf, 0x7f, 0x8d, 0x7c, 0x61, - 0x30, 0x77, 0x9c, 0x82, 0x74, 0xc7, 0x09, 0x5d, 0x71, 0xbe, 0xa8, 0xb9, 0xe2, 0xbc, 0xd8, 0x7f, - 0x9c, 0x84, 0x89, 0x35, 0xfe, 0x0a, 0xf2, 0x9f, 0x64, 0xe0, 0x5c, 0x5f, 0x0a, 0xf2, 0x1c, 0x14, - 0x4a, 0x1b, 0xd5, 0xcd, 0x68, 0x66, 0xd9, 0xd7, 0x22, 0x21, 0xe4, 0x16, 0x8c, 0x2e, 0xd9, 0xbe, - 0x53, 0x67, 0x0b, 0x38, 0xd5, 0x68, 0x94, 0x60, 0x1b, 0xa2, 0xaf, 0x3c, 0x63, 0x46, 0xb4, 0xc4, - 0x82, 0x69, 0xfc, 0x0a, 0x12, 0xaf, 0x8c, 0xc6, 0x0d, 0x06, 0x09, 0x86, 0x09, 0x32, 0x26, 0x61, - 0x12, 0xc0, 0xf8, 0xc7, 0xf7, 0x40, 0x6a, 0x21, 0xbd, 0x1b, 0x78, 0x82, 0x9c, 0x5b, 0x97, 0xa0, - 0xb0, 0x21, 0x2f, 0x3f, 0x94, 0x47, 0xc6, 0xe5, 0x45, 0x87, 0x19, 0x96, 0x1a, 0x7f, 0x2d, 0x23, - 0x4f, 0xf5, 0xc7, 0x77, 0x44, 0x49, 0xab, 0xdf, 0xe8, 0x9f, 0x56, 0xbf, 0xf1, 0x09, 0xd3, 0xea, - 0x1b, 0xbf, 0x28, 0x32, 0x62, 0x56, 0x1b, 0x1b, 0xb1, 0xe7, 0x99, 0x1e, 0xd7, 0xb9, 0x70, 0x59, - 0x5b, 0x9d, 0x17, 0x94, 0xf7, 0x39, 0x92, 0x75, 0xf5, 0xf6, 0x31, 0x54, 0x96, 0xea, 0x3f, 0xcd, - 0xc2, 0x73, 0xfd, 0xc8, 0x53, 0x5f, 0x92, 0xca, 0x9c, 0xec, 0x25, 0xa9, 0xcb, 0x50, 0xe0, 0x30, - 0xfd, 0x45, 0x5c, 0x41, 0xca, 0x06, 0x5c, 0x16, 0x93, 0x0b, 0x30, 0x5c, 0x2a, 0xd7, 0xa2, 0xb7, - 0x08, 0xd0, 0x1b, 0xc6, 0xae, 0xfb, 0xe8, 0x67, 0x21, 0x8a, 0xc8, 0xd7, 0x93, 0xcf, 0x6f, 0x88, - 0x47, 0x08, 0x9e, 0x55, 0x06, 0x24, 0x91, 0xac, 0x16, 0xdb, 0x1b, 0x25, 0x57, 0x15, 0xf9, 0x0a, - 0xcd, 0xe4, 0x53, 0x1e, 0x06, 0x0c, 0x6f, 0x78, 0xd4, 0xa7, 0x81, 0xea, 0xa9, 0xd2, 0x41, 0x88, - 0x29, 0x4a, 0x84, 0x1f, 0x89, 0x7d, 0xc8, 0x63, 0x01, 0x87, 0xd5, 0x28, 0x6b, 0x74, 0x3c, 0x61, - 0x60, 0x53, 0x41, 0x31, 0xbe, 0x93, 0x81, 0xd9, 0xb4, 0x66, 0x91, 0xe7, 0x20, 0xdf, 0x4e, 0x7d, - 0x38, 0xa4, 0xcd, 0x23, 0x92, 0xc6, 0xf0, 0x35, 0xd2, 0x1d, 0xd7, 0x6b, 0xd9, 0x81, 0xea, 0x9e, - 0xa3, 0x80, 0x4d, 0x60, 0x3f, 0x6e, 0xe2, 0xdf, 0x64, 0x41, 0x0a, 0xdb, 0x5c, 0xe2, 0xa9, 0x11, - 0xfc, 0xcf, 0x28, 0x01, 0x54, 0x1b, 0x1b, 0xeb, 0x1d, 0x9e, 0xfb, 0xf4, 0x3a, 0xe4, 0x59, 0xb3, - 0x62, 0x8b, 0x91, 0x2d, 0x87, 0xd2, 0xdd, 0x55, 0x81, 0xc4, 0x5b, 0xe5, 0xdb, 0xad, 0xa6, 0x89, - 0xc8, 0xc6, 0x16, 0x4c, 0xea, 0x18, 0x64, 0x59, 0xcf, 0x96, 0x35, 0xb6, 0x58, 0x14, 0x9c, 0x96, - 0x5c, 0x97, 0xbb, 0x88, 0x2e, 0x9d, 0xfd, 0xee, 0xd1, 0x02, 0xb0, 0x9f, 0x9c, 0x26, 0x2d, 0x9b, - 0x96, 0xf1, 0xe3, 0x59, 0x98, 0x8d, 0x42, 0xcd, 0xe4, 0x27, 0xf1, 0xd4, 0xc6, 0x3d, 0x94, 0x34, - 0xbf, 0x7c, 0xa9, 0x31, 0x25, 0x3b, 0xd8, 0xc7, 0x1d, 0xf8, 0x16, 0xcc, 0xf5, 0xc2, 0x27, 0xaf, - 0x26, 0x5e, 0x0b, 0x17, 0xd9, 0x14, 0xc2, 0x67, 0xc5, 0x95, 0xc7, 0xc3, 0xff, 0x51, 0x06, 0xe6, - 0x85, 0x63, 0xe3, 0x5d, 0xdb, 0x69, 0x07, 0xb4, 0x6d, 0xb7, 0xeb, 0xf4, 0xc9, 0xc4, 0xed, 0xdc, - 0xd2, 0xc4, 0xd2, 0x4b, 0xba, 0xff, 0x6a, 0xa2, 0xb6, 0xde, 0xbd, 0x25, 0x97, 0x31, 0x6f, 0x46, - 0x9d, 0x2f, 0xde, 0x3c, 0x8f, 0x93, 0x6c, 0x33, 0x80, 0x1a, 0x27, 0x89, 0x18, 0xc6, 0xf7, 0xc3, - 0xf3, 0xfd, 0x2b, 0x20, 0x5f, 0x83, 0x89, 0xd2, 0x2e, 0x6d, 0x07, 0xf7, 0x3a, 0xbb, 0x9e, 0xdd, - 0xa0, 0xd2, 0xa6, 0x25, 0x4d, 0x8a, 0x6a, 0x19, 0xcf, 0x15, 0x22, 0xe2, 0xf6, 0x18, 0xdc, 0xea, - 0x0a, 0x22, 0xcd, 0x7b, 0x58, 0xe5, 0x66, 0x7c, 0x3b, 0x03, 0x24, 0xc9, 0x83, 0xdc, 0x80, 0xf1, - 0x7b, 0x9b, 0xe5, 0x5a, 0x60, 0x7b, 0xc1, 0x8a, 0xdb, 0xf5, 0x44, 0x0e, 0x0e, 0x1e, 0xc6, 0x15, - 0xd4, 0x99, 0x64, 0xf0, 0x02, 0x6b, 0xcf, 0xed, 0x7a, 0xa6, 0x86, 0x87, 0x09, 0xed, 0x29, 0xdd, - 0x6f, 0xd8, 0x87, 0x7a, 0x42, 0x7b, 0x01, 0xd3, 0x12, 0xda, 0x0b, 0xd8, 0xff, 0x47, 0xdd, 0xd5, - 0xfc, 0x38, 0x52, 0x5c, 0xf1, 0x6d, 0xdb, 0x33, 0x3b, 0xf3, 0x3c, 0x1f, 0x3d, 0xc5, 0xb2, 0x3b, - 0xd9, 0x8f, 0x61, 0x71, 0x96, 0x85, 0x35, 0xb0, 0xb0, 0x6c, 0x08, 0x2c, 0x09, 0x81, 0x1e, 0xbb, - 0x3d, 0x6e, 0xc6, 0xee, 0x36, 0xdd, 0xed, 0x19, 0x16, 0x48, 0x5a, 0x66, 0xa6, 0x77, 0xb6, 0x13, - 0x4f, 0xdb, 0xd8, 0x6d, 0x96, 0xe5, 0x96, 0x0b, 0x87, 0x44, 0x44, 0x09, 0xca, 0x21, 0x4a, 0x22, - 0x45, 0x91, 0xf8, 0x2f, 0xf2, 0x0f, 0x20, 0xa1, 0x48, 0x1c, 0x72, 0x8b, 0x84, 0x92, 0x95, 0x72, - 0xc9, 0x35, 0xca, 0x85, 0x53, 0x54, 0xaf, 0xaa, 0xba, 0xab, 0xdb, 0x1f, 0xcc, 0xec, 0x42, 0xa2, - 0xdc, 0xec, 0xaa, 0xf7, 0xaa, 0xeb, 0xbb, 0xde, 0xab, 0x57, 0xef, 0xf7, 0x4a, 0x9f, 0x28, 0x70, - 0x4e, 0xbc, 0x78, 0xd8, 0x9f, 0x50, 0x97, 0x0a, 0x3a, 0x2b, 0x0f, 0x04, 0x06, 0xd9, 0x2c, 0xd9, - 0x74, 0x4d, 0xf8, 0xf3, 0x63, 0x05, 0x51, 0x48, 0x65, 0xbc, 0xe4, 0x55, 0x28, 0x38, 0x51, 0xaf, - 0x7f, 0x04, 0x87, 0x7e, 0x35, 0x1e, 0xd1, 0xa8, 0xd7, 0xc7, 0x22, 0x90, 0xb3, 0xe4, 0xc3, 0x29, - 0xb9, 0x72, 0xa2, 0xc6, 0xa4, 0x09, 0x27, 0x39, 0x48, 0x4b, 0xc6, 0x3a, 0x34, 0xa3, 0x4d, 0x9b, - 0xab, 0x02, 0x35, 0x80, 0x63, 0x60, 0xd9, 0xa2, 0x8c, 0xd2, 0x2f, 0x14, 0x28, 0x52, 0xe1, 0x01, - 0xd5, 0xb1, 0x07, 0x9d, 0xd2, 0x69, 0x39, 0x50, 0x18, 0x17, 0xe3, 0xe2, 0x8f, 0x74, 0xb8, 0x3e, - 0x0f, 0xab, 0x19, 0x06, 0x52, 0x42, 0x7f, 0xd1, 0x6e, 0xb0, 0xd7, 0x61, 0xf8, 0xd8, 0xcc, 0x32, - 0x97, 0x4a, 0x2b, 0xfd, 0x4c, 0x81, 0x53, 0x54, 0x79, 0x37, 0xf0, 0xde, 0xd6, 0x1e, 0x75, 0xc5, - 0x7a, 0xa7, 0x02, 0x91, 0x78, 0x3a, 0xc3, 0x7c, 0xd9, 0x98, 0x40, 0xc4, 0xd3, 0xec, 0x38, 0x97, - 0xd4, 0x61, 0x81, 0x9f, 0x2f, 0x43, 0x0e, 0x5d, 0xb5, 0x21, 0xdd, 0x0a, 0x24, 0x05, 0x73, 0x22, - 0xda, 0x12, 0xdc, 0xc2, 0x38, 0x8f, 0x1d, 0x73, 0x97, 0xfe, 0xa5, 0xc0, 0x99, 0x29, 0x3c, 0xe4, - 0x65, 0x98, 0xc3, 0x27, 0xf9, 0x7c, 0xf4, 0xce, 0x4f, 0xf9, 0x44, 0xb4, 0x77, 0x7b, 0xe7, 0x1a, - 0x3b, 0x88, 0x0e, 0xe9, 0x1f, 0x9b, 0x71, 0x91, 0xb7, 0x60, 0x51, 0xdb, 0xdf, 0xe7, 0x7a, 0x49, - 0x2e, 0xa5, 0x97, 0x4c, 0xf9, 0xe2, 0xd5, 0x98, 0x9e, 0xe9, 0x25, 0xec, 0x71, 0xe8, 0xfe, 0xbe, - 0xc7, 0xdd, 0x0d, 0x92, 0xf2, 0xce, 0x7e, 0x1f, 0x56, 0xd2, 0xc4, 0xc7, 0xd2, 0x4b, 0x7e, 0xa3, - 0x80, 0x9a, 0xae, 0xc3, 0x37, 0x83, 0x77, 0x30, 0x69, 0x98, 0xbf, 0x62, 0x52, 0xfd, 0x2a, 0x07, - 0x0f, 0x4f, 0xec, 0x61, 0xf2, 0x34, 0xcc, 0x6b, 0xfd, 0xbe, 0x51, 0xe5, 0xb3, 0x8a, 0x0b, 0x3c, - 0x78, 0xdd, 0x9b, 0x52, 0xdb, 0x18, 0x11, 0xb9, 0x0e, 0x0b, 0x38, 0x33, 0x29, 0x43, 0x2e, 0x01, - 0x91, 0x62, 0xb7, 0x21, 0x19, 0x10, 0x29, 0x41, 0x48, 0x6a, 0xb0, 0xc2, 0x5d, 0x9f, 0x6d, 0xff, - 0xc0, 0x7f, 0x3f, 0x46, 0x33, 0x45, 0xc0, 0x55, 0x71, 0x87, 0xec, 0x0d, 0x58, 0x9e, 0xec, 0xfc, - 0x9b, 0xe6, 0xc2, 0xe8, 0xf5, 0xb4, 0x4c, 0xb9, 0x24, 0x86, 0x64, 0xc5, 0xa2, 0xd7, 0x63, 0x25, - 0xa6, 0x94, 0x35, 0xc6, 0x19, 0x0f, 0x97, 0x36, 0x1c, 0x06, 0x07, 0xe1, 0xa1, 0x1f, 0x46, 0xdf, - 0xdc, 0x70, 0x25, 0xdf, 0x38, 0xd2, 0x70, 0xfd, 0xba, 0xc0, 0x16, 0x73, 0x96, 0x8d, 0x4a, 0x34, - 0x12, 0x78, 0x21, 0x4a, 0x34, 0x18, 0xfa, 0x91, 0x39, 0xf7, 0x56, 0xe1, 0x24, 0x73, 0xba, 0x16, - 0x2b, 0xe3, 0xc2, 0xc4, 0x2a, 0x30, 0x9a, 0x9d, 0x6b, 0x4c, 0x7c, 0x61, 0xbe, 0x01, 0x43, 0x5b, - 0xb0, 0x92, 0x1d, 0x28, 0x56, 0xba, 0x7e, 0x27, 0x1c, 0xf5, 0xdd, 0xa3, 0x99, 0x01, 0xd7, 0x79, - 0x5b, 0x96, 0xf6, 0x18, 0x1b, 0x9a, 0x0f, 0x71, 0x27, 0x97, 0x0b, 0x22, 0x6e, 0xfc, 0x5c, 0xb8, - 0x80, 0x57, 0x8e, 0xcf, 0xce, 0xe8, 0x9f, 0x6c, 0x22, 0xf2, 0xa5, 0xdf, 0xc2, 0xf3, 0xf7, 0xc4, - 0x1e, 0xac, 0x34, 0x3a, 0xc3, 0xc8, 0x1d, 0x74, 0xc2, 0x21, 0x42, 0x2e, 0x1d, 0x01, 0xcc, 0x42, - 0x04, 0x89, 0x65, 0x57, 0x91, 0x51, 0xcc, 0xca, 0xae, 0x22, 0xd3, 0xc5, 0x51, 0x79, 0xa9, 0x16, - 0x84, 0x9d, 0x6e, 0xf0, 0x81, 0xf0, 0xaa, 0x60, 0xf2, 0xd2, 0x2d, 0x91, 0x68, 0x27, 0xf9, 0xa5, - 0xb7, 0xc7, 0xc6, 0x8d, 0xd5, 0xb2, 0x08, 0x27, 0xb9, 0x23, 0x1d, 0x73, 0x2c, 0x6b, 0xe9, 0x66, - 0xd5, 0x30, 0xb7, 0x54, 0x85, 0xac, 0x00, 0xb4, 0x6c, 0xab, 0xa2, 0x3b, 0x0e, 0xfd, 0x9f, 0xa3, - 0xff, 0xb9, 0xd7, 0x59, 0xad, 0xdd, 0x50, 0xf3, 0x92, 0xe3, 0x59, 0xa1, 0xf4, 0x99, 0x02, 0xa7, - 0x27, 0x0f, 0x25, 0x71, 0x01, 0x5d, 0x0f, 0xb9, 0x41, 0xf8, 0xbb, 0x33, 0xc7, 0x7d, 0x62, 0x72, - 0xd6, 0x85, 0x31, 0x62, 0xae, 0x71, 0x39, 0x61, 0xf5, 0x49, 0xc2, 0x9b, 0x06, 0xfb, 0xa5, 0x0a, - 0xac, 0x4f, 0x2b, 0x23, 0xdd, 0xd4, 0x55, 0x28, 0x6a, 0xad, 0x56, 0xc3, 0xa8, 0x68, 0xae, 0x61, - 0x99, 0xaa, 0x42, 0x16, 0x61, 0x6e, 0xcb, 0xb6, 0xda, 0x2d, 0x35, 0x57, 0xfa, 0x58, 0x81, 0x65, - 0x23, 0x8c, 0xfc, 0x03, 0xf6, 0x4a, 0xf5, 0x41, 0x17, 0xdf, 0x4b, 0xa9, 0xc5, 0xb7, 0x1e, 0x3b, - 0xe9, 0xc6, 0x1f, 0x38, 0xd2, 0xca, 0xfb, 0x8b, 0x02, 0x6b, 0x63, 0x3c, 0xc4, 0x81, 0x93, 0xda, - 0xae, 0x63, 0x19, 0xd5, 0x0a, 0xaf, 0x99, 0x90, 0xca, 0x79, 0xea, 0xf8, 0x57, 0x98, 0x0f, 0xcc, - 0x9d, 0xa1, 0xd7, 0x0b, 0xf6, 0xa5, 0xf8, 0x4c, 0xf5, 0x13, 0xb6, 0x28, 0x09, 0x4f, 0xb2, 0x0f, - 0x46, 0x03, 0x1f, 0x8b, 0xcd, 0xa5, 0x6e, 0x34, 0xe3, 0xf4, 0xf1, 0x82, 0xf1, 0x51, 0x66, 0x87, - 0xe6, 0x8f, 0x17, 0x9d, 0x94, 0xb7, 0xb9, 0x0c, 0x45, 0xae, 0xb5, 0xa0, 0x42, 0xf0, 0x91, 0x02, - 0xeb, 0xd3, 0xea, 0x4a, 0x15, 0xa1, 0xb4, 0x43, 0xdc, 0xe9, 0x18, 0xac, 0x37, 0xed, 0x09, 0x27, - 0xc8, 0xc8, 0x2b, 0x50, 0x64, 0x31, 0xa3, 0x9d, 0xeb, 0x6d, 0xdb, 0xe0, 0x13, 0xe4, 0xc2, 0x3f, - 0xbf, 0x78, 0xe4, 0x0c, 0x8b, 0x30, 0xed, 0x0d, 0xaf, 0x7b, 0xa3, 0x41, 0x90, 0x02, 0x36, 0x95, - 0x39, 0x4a, 0x1f, 0x2a, 0x70, 0x76, 0x7a, 0x23, 0xe9, 0x29, 0xe3, 0x52, 0xd9, 0x3c, 0xf1, 0x29, - 0xc2, 0x53, 0x06, 0xe5, 0xf5, 0x8c, 0x53, 0x51, 0x4c, 0x48, 0x99, 0xe2, 0xc8, 0x87, 0xb9, 0xb1, - 0x80, 0x67, 0x69, 0x26, 0x41, 0x58, 0xfa, 0x5d, 0x0e, 0x4e, 0xd3, 0x09, 0xd4, 0xf5, 0x87, 0x43, - 0x6d, 0x14, 0xdd, 0xf6, 0xc3, 0x88, 0x8b, 0x54, 0xe4, 0x05, 0x98, 0xbf, 0x7d, 0xbc, 0xdb, 0x40, - 0x46, 0x4e, 0x08, 0xe0, 0xa6, 0x2c, 0x1e, 0x75, 0xd2, 0xdf, 0xe4, 0x02, 0x48, 0x01, 0xe6, 0x70, - 0x4f, 0x5d, 0xb2, 0x17, 0xfb, 0x71, 0x98, 0xb9, 0x17, 0x61, 0x0e, 0xb5, 0x7f, 0xbe, 0x35, 0x0a, - 0x91, 0x76, 0x72, 0xcd, 0xf0, 0x6e, 0xc0, 0x66, 0x0c, 0xe4, 0x19, 0x80, 0x04, 0x14, 0x96, 0xef, - 0x7d, 0x42, 0x8d, 0x8e, 0x71, 0x61, 0xed, 0xc5, 0xc3, 0x5b, 0x1d, 0x8e, 0xb4, 0x5a, 0x86, 0x35, - 0xd1, 0x25, 0x7d, 0x81, 0x5d, 0xc3, 0xcd, 0x53, 0xab, 0x2c, 0xc3, 0xe8, 0x73, 0xfc, 0x9a, 0xd2, - 0x3f, 0x72, 0xb0, 0xb8, 0x4b, 0x05, 0x05, 0x54, 0x7f, 0x67, 0xab, 0xd3, 0xcf, 0x41, 0xb1, 0xd1, - 0xeb, 0xf0, 0xbb, 0xfb, 0x21, 0x87, 0xcf, 0x42, 0xc7, 0x9a, 0x6e, 0xaf, 0x23, 0xcc, 0x00, 0x43, - 0x5b, 0x26, 0xfa, 0x0a, 0xa7, 0xa0, 0xd7, 0x60, 0x9e, 0xd9, 0x52, 0xf8, 0x45, 0x8d, 0x10, 0x15, - 0xe3, 0x1a, 0x5d, 0x65, 0xd9, 0xd2, 0x75, 0x33, 0xb3, 0xc7, 0xc8, 0x72, 0x0b, 0x47, 0xcf, 0x92, - 0x94, 0xfd, 0xb9, 0xa3, 0x29, 0xfb, 0x12, 0x4a, 0xc8, 0xfc, 0x51, 0x50, 0x42, 0xce, 0xde, 0x80, - 0xa2, 0x54, 0x9f, 0x63, 0x49, 0x8e, 0x3f, 0xcd, 0xc1, 0x32, 0xb6, 0x2a, 0x7e, 0x58, 0xf1, 0xff, - 0x79, 0x75, 0xf1, 0x52, 0xea, 0xea, 0x62, 0x5d, 0x1e, 0x2f, 0xd6, 0xb2, 0x19, 0x77, 0x16, 0xaf, - 0xc1, 0xda, 0x18, 0x21, 0x79, 0x1e, 0xe6, 0x68, 0xf5, 0x85, 0xaa, 0xa7, 0x66, 0x67, 0x40, 0x82, - 0x28, 0x47, 0x1b, 0x3e, 0xb4, 0x19, 0x75, 0xe9, 0xdf, 0x0a, 0x2c, 0x71, 0x94, 0xe0, 0xf0, 0x56, - 0xef, 0x2b, 0xbb, 0xf3, 0x72, 0xb6, 0x3b, 0x99, 0x8b, 0x2b, 0xef, 0xce, 0xff, 0x76, 0x27, 0xde, - 0x48, 0x75, 0xe2, 0x99, 0x18, 0x5f, 0x46, 0x34, 0x67, 0x46, 0x1f, 0xfe, 0x09, 0x11, 0xd7, 0xd2, - 0x84, 0xe4, 0x47, 0xb0, 0x68, 0xfa, 0x77, 0x52, 0x1a, 0xd3, 0xe5, 0x29, 0x85, 0x5e, 0x8d, 0x09, - 0xd9, 0x9a, 0xc2, 0xc3, 0x26, 0xf4, 0xef, 0x78, 0x63, 0x66, 0x9c, 0xa4, 0x48, 0xaa, 0x34, 0xa5, - 0xd9, 0x8e, 0x33, 0xf5, 0xb9, 0x23, 0x05, 0x7a, 0x6d, 0xff, 0xb1, 0x00, 0x90, 0xbc, 0x41, 0xa7, - 0x0b, 0x30, 0x65, 0xc1, 0x16, 0x77, 0xc7, 0x98, 0x24, 0xcf, 0x71, 0x61, 0xd8, 0xbe, 0xcc, 0x2f, - 0x45, 0x73, 0xd3, 0xf1, 0x7f, 0xf0, 0x7a, 0xb4, 0xc2, 0xdf, 0x78, 0xef, 0xfb, 0xdd, 0x0e, 0xdb, - 0x8b, 0xf3, 0x9b, 0x97, 0x10, 0xee, 0x2d, 0x4e, 0x9d, 0x12, 0xee, 0x0d, 0x5f, 0x82, 0x57, 0x29, - 0xc1, 0x98, 0x5f, 0x47, 0xe1, 0xfe, 0xfd, 0x3a, 0xe6, 0xee, 0xc3, 0xaf, 0x63, 0xfe, 0x88, 0x7e, - 0x1d, 0x2d, 0x58, 0x0c, 0xc2, 0xf7, 0xfc, 0x30, 0xea, 0x0d, 0xee, 0xa2, 0x95, 0x3a, 0xb9, 0xca, - 0xa2, 0x5d, 0x6d, 0x88, 0x3c, 0x36, 0xde, 0x78, 0x60, 0xc6, 0xf4, 0xf2, 0x70, 0xc7, 0x89, 0xe4, - 0x3b, 0x90, 0x58, 0x3d, 0x38, 0xb4, 0xf7, 0xf4, 0x73, 0x76, 0x4f, 0x18, 0x45, 0x5e, 0x85, 0xb4, - 0xf1, 0x83, 0x7b, 0x45, 0xb2, 0x78, 0xa2, 0x72, 0x86, 0x0c, 0x79, 0xb5, 0x27, 0xd9, 0x47, 0xf8, - 0xb3, 0xd7, 0x2f, 0x73, 0x40, 0xc6, 0x2b, 0x4e, 0x5e, 0x82, 0x22, 0xdb, 0xfa, 0xbd, 0xc1, 0xf0, - 0x5d, 0xee, 0x8c, 0xc0, 0xbc, 0xf2, 0xa5, 0x64, 0xd9, 0x2b, 0x9f, 0x25, 0xdb, 0xc3, 0x77, 0xbb, - 0xe4, 0x87, 0xf0, 0x10, 0x0e, 0x7c, 0xdf, 0x1f, 0x04, 0xbd, 0x7d, 0x0f, 0x71, 0xd1, 0x3a, 0x5d, - 0x1e, 0x34, 0xe6, 0x69, 0x8c, 0x6e, 0x36, 0x9e, 0x3d, 0x65, 0x82, 0xe0, 0x9b, 0xff, 0x16, 0x52, - 0xb6, 0x18, 0x21, 0x71, 0x41, 0x95, 0xf9, 0x6f, 0x8d, 0xba, 0x5d, 0x3e, 0xe7, 0xca, 0x54, 0xfd, - 0xcd, 0xe6, 0x4d, 0x29, 0x78, 0x25, 0x29, 0xb8, 0x36, 0xea, 0x76, 0xc9, 0x0b, 0x00, 0xbd, 0xd0, - 0x3b, 0x0c, 0x86, 0x43, 0x66, 0xc8, 0x88, 0xfd, 0x75, 0x92, 0x54, 0x79, 0xf8, 0x7a, 0x61, 0x93, - 0x25, 0xd2, 0xe1, 0xeb, 0x77, 0x0e, 0x7c, 0xf4, 0x72, 0x65, 0x8f, 0x56, 0x38, 0x0c, 0xb4, 0x48, - 0x4c, 0x4f, 0xa3, 0x03, 0xdf, 0x09, 0x3e, 0x10, 0x6f, 0x8e, 0xdf, 0x84, 0x35, 0xfe, 0x5c, 0x74, - 0x37, 0x88, 0x6e, 0x73, 0xb9, 0xfb, 0x41, 0x84, 0x76, 0x49, 0xf0, 0xfe, 0x6b, 0x01, 0x40, 0xdb, - 0x75, 0x04, 0x80, 0xc4, 0x15, 0x98, 0xa3, 0xda, 0x84, 0xb8, 0x95, 0xc0, 0x3b, 0x5d, 0x2c, 0x57, - 0xbe, 0xd3, 0x45, 0x0a, 0xba, 0x4f, 0xd8, 0xfe, 0x01, 0x5e, 0x8c, 0xe5, 0x92, 0x2b, 0x8c, 0x01, - 0x4b, 0x4a, 0x49, 0xaf, 0x2c, 0x89, 0x34, 0x00, 0x12, 0x48, 0x07, 0xae, 0xdf, 0xae, 0x25, 0xbe, - 0xd1, 0x3c, 0x83, 0xe3, 0xfb, 0x26, 0xb0, 0x10, 0xf2, 0xf4, 0x49, 0xc8, 0xc8, 0x36, 0x14, 0xdc, - 0x4e, 0xec, 0x8d, 0x32, 0x05, 0xe8, 0xe2, 0x22, 0x0f, 0xea, 0x93, 0x80, 0x5d, 0xac, 0x44, 0x9d, - 0x54, 0xec, 0x33, 0x2c, 0x84, 0xe8, 0x30, 0xcf, 0x03, 0x36, 0x4e, 0x41, 0x3d, 0xe2, 0xf1, 0x1a, - 0x39, 0xd6, 0x21, 0x26, 0xca, 0xd2, 0x0e, 0x0f, 0xcd, 0xf8, 0x1c, 0xe4, 0x1d, 0xa7, 0xc9, 0xdd, - 0x3b, 0x97, 0x13, 0x5d, 0xc5, 0x71, 0x9a, 0x22, 0x28, 0xed, 0xa1, 0xc4, 0x46, 0x89, 0xc9, 0xf7, - 0xa0, 0x28, 0x09, 0xe2, 0xdc, 0x31, 0x1a, 0xfb, 0x20, 0x48, 0x92, 0xe5, 0xed, 0x4c, 0xa2, 0x26, - 0x0d, 0x50, 0xb7, 0x47, 0xef, 0xf8, 0x5a, 0xbf, 0x8f, 0x9e, 0x14, 0xef, 0xf9, 0x03, 0x86, 0x3d, - 0xbc, 0x90, 0xc0, 0x04, 0x7a, 0x9d, 0x7e, 0xdf, 0xdb, 0x17, 0xb9, 0xf2, 0xcd, 0x4c, 0x96, 0x93, - 0xb4, 0x60, 0xcd, 0xf1, 0xa3, 0x51, 0x9f, 0x3d, 0xc3, 0xa8, 0xf5, 0x06, 0x54, 0x35, 0x61, 0x1b, - 0x06, 0x22, 0xaa, 0x0d, 0x69, 0xa6, 0x78, 0xfb, 0x72, 0xab, 0x37, 0xc8, 0xa8, 0x29, 0xe3, 0xcc, - 0x25, 0x5f, 0x1e, 0x72, 0x7a, 0xde, 0xa7, 0x15, 0x1e, 0x3c, 0xef, 0x85, 0xc2, 0x93, 0xa8, 0x39, - 0xcf, 0x4c, 0x80, 0xfa, 0x40, 0x33, 0x9a, 0x04, 0xf5, 0x91, 0x02, 0xf8, 0xf8, 0xa4, 0x20, 0x41, - 0x48, 0xf1, 0xb1, 0x78, 0x19, 0xe0, 0xb5, 0x5e, 0x10, 0x36, 0xfd, 0xe8, 0x76, 0x6f, 0x5f, 0x42, - 0x1c, 0x29, 0xfe, 0xb8, 0x17, 0x84, 0xde, 0x21, 0x26, 0x7f, 0xf9, 0xc5, 0x23, 0x12, 0x91, 0x2d, - 0xfd, 0x26, 0x4f, 0xc1, 0x22, 0xfd, 0xe7, 0x26, 0x8f, 0x49, 0xd8, 0x05, 0x26, 0x72, 0xf3, 0xe0, - 0xd6, 0x31, 0x01, 0xb9, 0x81, 0x00, 0xe1, 0x41, 0x3f, 0x92, 0xc4, 0x6a, 0x81, 0x06, 0x1e, 0xf4, - 0xa3, 0x2c, 0x2a, 0xa0, 0x44, 0x4c, 0xea, 0x71, 0xd5, 0x05, 0xc4, 0x3c, 0xc7, 0x21, 0xc7, 0x5b, - 0x3a, 0x3e, 0xd7, 0x3c, 0x01, 0x47, 0x26, 0x07, 0x03, 0xcb, 0xb0, 0x61, 0x25, 0x9c, 0x7a, 0x95, - 0x99, 0x55, 0xf8, 0xe9, 0xc6, 0x2a, 0x31, 0xbc, 0xbd, 0xef, 0xed, 0x61, 0x72, 0xaa, 0x12, 0x31, - 0x31, 0xd9, 0x84, 0x55, 0xe6, 0x17, 0x1f, 0x87, 0xaa, 0xe1, 0x27, 0x1d, 0xee, 0x6d, 0x49, 0x2c, - 0x1b, 0xf9, 0xf3, 0x19, 0x06, 0x52, 0x83, 0x39, 0x54, 0x2d, 0xf9, 0x63, 0xf0, 0x73, 0xb2, 0x4e, - 0x9d, 0x5d, 0x47, 0xb8, 0xaf, 0xa0, 0x36, 0x2d, 0xef, 0x2b, 0x48, 0x4a, 0xde, 0x00, 0xd0, 0xc3, - 0x41, 0xaf, 0xdb, 0x45, 0xc0, 0xbc, 0x05, 0x54, 0xcc, 0x2e, 0xa4, 0xd7, 0x23, 0x96, 0x92, 0x10, - 0x71, 0x1c, 0x18, 0xfc, 0xef, 0x65, 0x60, 0xf5, 0xa4, 0xb2, 0x4a, 0x06, 0xcc, 0xb3, 0xc5, 0x88, - 0xe0, 0x93, 0x1c, 0x14, 0x5b, 0x82, 0x2e, 0x64, 0xe0, 0x93, 0x3c, 0x7d, 0x1c, 0x7c, 0x52, 0x62, - 0x28, 0x6d, 0xc3, 0xa9, 0x49, 0x0d, 0x4b, 0x29, 0xc3, 0xca, 0x51, 0x95, 0xe1, 0x3f, 0xe4, 0x61, - 0x09, 0x4b, 0x13, 0xbb, 0xb0, 0x06, 0xcb, 0xce, 0xe8, 0x9d, 0x18, 0xc4, 0x41, 0xec, 0xc6, 0x58, - 0xbf, 0xa1, 0x9c, 0x21, 0x1b, 0xbc, 0x52, 0x1c, 0x44, 0x87, 0x15, 0x71, 0x12, 0x6c, 0x89, 0x07, - 0xe6, 0x31, 0xee, 0xa3, 0x80, 0x17, 0x1a, 0x0f, 0xd5, 0x95, 0x61, 0x4a, 0xce, 0x83, 0xfc, 0x71, - 0xce, 0x83, 0xc2, 0x91, 0xce, 0x83, 0xb7, 0x60, 0x49, 0x7c, 0x0d, 0x77, 0xf2, 0xb9, 0x07, 0xdb, - 0xc9, 0x53, 0x85, 0x91, 0x46, 0xbc, 0xa3, 0xcf, 0xcf, 0xdc, 0xd1, 0xd1, 0x8a, 0x28, 0x56, 0xd9, - 0x58, 0xf4, 0x5d, 0x5e, 0x06, 0xc6, 0xb2, 0xd9, 0xaa, 0xb4, 0xee, 0xe3, 0x94, 0x7c, 0x1e, 0x16, - 0x1b, 0x3d, 0x61, 0x40, 0x92, 0x6e, 0xee, 0xbb, 0x22, 0x51, 0x16, 0x17, 0x62, 0xca, 0xf8, 0x74, - 0xcb, 0x7f, 0x1d, 0xa7, 0xdb, 0x0d, 0x00, 0xee, 0xb9, 0x90, 0xc4, 0xa0, 0xc0, 0x25, 0x23, 0x7c, - 0x74, 0xd3, 0x06, 0x04, 0x89, 0x98, 0xee, 0x4e, 0xfc, 0xa9, 0x89, 0xb6, 0xb7, 0xd7, 0x1b, 0x85, - 0x51, 0x2a, 0x68, 0x1b, 0x77, 0xd7, 0xa7, 0x47, 0x02, 0xe6, 0xc9, 0xdb, 0x43, 0x86, 0xed, 0xeb, - 0x1d, 0x10, 0xf2, 0x7a, 0xfc, 0x46, 0x6e, 0x66, 0x0c, 0xeb, 0xd2, 0x58, 0x0f, 0x4d, 0x7d, 0x19, - 0x57, 0xfa, 0x4c, 0x91, 0x41, 0x77, 0xef, 0x63, 0xa8, 0x5f, 0x04, 0x88, 0x2d, 0xf8, 0x62, 0xac, - 0x99, 0x26, 0x17, 0xa7, 0xca, 0xbd, 0x9c, 0xd0, 0x4a, 0xad, 0xc9, 0x7f, 0x5d, 0xad, 0x71, 0xa1, - 0x68, 0xfd, 0x24, 0xea, 0x24, 0x4f, 0x3e, 0xc0, 0x89, 0x25, 0x59, 0xdc, 0x99, 0x44, 0xac, 0xed, - 0x44, 0x0e, 0x9e, 0x1a, 0x6b, 0x3b, 0x66, 0x2c, 0xbd, 0x0e, 0xab, 0xb2, 0x23, 0xe1, 0xdd, 0x70, - 0x8f, 0xfc, 0x80, 0xa1, 0x85, 0x29, 0x29, 0x1d, 0x47, 0x22, 0xa2, 0x3b, 0xee, 0xdd, 0x70, 0x8f, - 0xc9, 0x3f, 0x9d, 0x3b, 0x72, 0x5d, 0x51, 0xfb, 0xfc, 0x5c, 0x01, 0x32, 0x4e, 0x2e, 0xef, 0x26, - 0xca, 0xff, 0x40, 0xba, 0xcc, 0x48, 0x65, 0x85, 0xe3, 0x48, 0x65, 0xe5, 0x5f, 0x2a, 0xb0, 0x6a, - 0x68, 0x4d, 0x8e, 0x90, 0xcb, 0x2c, 0x11, 0x8f, 0xc2, 0x05, 0x43, 0x6b, 0x7a, 0x2d, 0xab, 0x61, - 0x54, 0x6e, 0x7a, 0x13, 0x81, 0xef, 0x2e, 0xc0, 0xb7, 0xc6, 0x49, 0x12, 0x8b, 0xc5, 0x79, 0x58, - 0x1f, 0xcf, 0x16, 0xe0, 0x78, 0x93, 0x99, 0x05, 0x8e, 0x5e, 0xbe, 0xfc, 0x0a, 0xac, 0x0a, 0xcc, - 0x38, 0xb7, 0xe1, 0x20, 0xd4, 0xec, 0x2a, 0x14, 0x77, 0x74, 0xdb, 0xa8, 0xdd, 0xf4, 0x6a, 0xed, - 0x46, 0x43, 0x3d, 0x41, 0x96, 0x61, 0x91, 0x27, 0x54, 0x34, 0x55, 0x21, 0x4b, 0xb0, 0x60, 0x98, - 0x8e, 0x5e, 0x69, 0xdb, 0xba, 0x9a, 0x2b, 0xbf, 0x02, 0x2b, 0xad, 0x41, 0xf0, 0x5e, 0x27, 0xf2, - 0xb7, 0xfd, 0xbb, 0x68, 0x70, 0x38, 0x09, 0x79, 0x5b, 0xdb, 0x55, 0x4f, 0x10, 0x80, 0xf9, 0xd6, - 0x76, 0xc5, 0xb9, 0x76, 0x4d, 0x55, 0x48, 0x11, 0x4e, 0x6e, 0x55, 0x5a, 0xde, 0x76, 0xd3, 0x51, - 0x73, 0xf4, 0x8f, 0xb6, 0xeb, 0xe0, 0x9f, 0x7c, 0xf9, 0x59, 0x58, 0x43, 0x59, 0xa1, 0x11, 0x0c, - 0x23, 0x3f, 0xf4, 0x07, 0x58, 0x87, 0x25, 0x58, 0x70, 0x7c, 0xba, 0xc8, 0x23, 0x9f, 0x55, 0xa0, - 0x39, 0xea, 0x46, 0x41, 0xbf, 0xeb, 0xbf, 0xaf, 0x2a, 0xe5, 0x1b, 0xb0, 0x6a, 0xf7, 0x46, 0x51, - 0x10, 0x1e, 0x38, 0x11, 0xa5, 0x38, 0xb8, 0x4b, 0x1e, 0x86, 0xb5, 0xb6, 0xa9, 0x35, 0x37, 0x8d, - 0xad, 0xb6, 0xd5, 0x76, 0xbc, 0xa6, 0xe6, 0x56, 0xea, 0xcc, 0xdc, 0xd1, 0xb4, 0x1c, 0xd7, 0xb3, - 0xf5, 0x8a, 0x6e, 0xba, 0xaa, 0x52, 0xfe, 0xb9, 0x02, 0x2b, 0xed, 0x21, 0x7f, 0xa2, 0xdb, 0x46, - 0x47, 0xbb, 0x8b, 0x70, 0xbe, 0xed, 0xe8, 0xb6, 0xe7, 0x5a, 0xdb, 0xba, 0xe9, 0xb5, 0x1d, 0x6d, - 0x2b, 0x8b, 0xba, 0xf8, 0x08, 0x9c, 0x93, 0x28, 0x6c, 0xbd, 0x62, 0xed, 0xe8, 0xb6, 0xd7, 0xd2, - 0x1c, 0x67, 0xd7, 0xb2, 0xab, 0xaa, 0x42, 0xce, 0xc2, 0xe9, 0x09, 0x04, 0xcd, 0x9a, 0xa6, 0xe6, - 0xc6, 0xf2, 0x4c, 0x7d, 0x57, 0x6b, 0x78, 0x9b, 0x96, 0xab, 0xe6, 0xcb, 0x4d, 0x7a, 0xd0, 0x21, - 0x30, 0x19, 0x83, 0x95, 0x5f, 0x80, 0x82, 0x69, 0x99, 0x7a, 0xd6, 0x24, 0xb5, 0x04, 0x0b, 0x5a, - 0xab, 0x65, 0x5b, 0x3b, 0x38, 0xa0, 0x00, 0xf3, 0x55, 0xdd, 0xa4, 0x35, 0xcb, 0xd3, 0x9c, 0x96, - 0x6d, 0x35, 0x2d, 0x57, 0xaf, 0xaa, 0x85, 0xb2, 0x2d, 0x16, 0x8c, 0x28, 0x74, 0xaf, 0xc7, 0xec, - 0x3f, 0x55, 0xbd, 0xa6, 0xb5, 0x1b, 0x2e, 0xef, 0x90, 0x9b, 0x9e, 0xad, 0xbf, 0xde, 0xd6, 0x1d, - 0xd7, 0x51, 0x15, 0xa2, 0xc2, 0x92, 0xa9, 0xeb, 0x55, 0xc7, 0xb3, 0xf5, 0x1d, 0x43, 0xdf, 0x55, - 0x73, 0xb4, 0x4c, 0xf6, 0x9b, 0x7e, 0xa1, 0xfc, 0x89, 0x02, 0x84, 0x81, 0xba, 0x09, 0xf8, 0x6f, - 0x1c, 0x9f, 0x0d, 0x38, 0x5b, 0xa7, 0x1d, 0x8b, 0x4d, 0x6b, 0x5a, 0xd5, 0x6c, 0x97, 0x9d, 0x06, - 0x92, 0xc9, 0xb7, 0x6a, 0x35, 0x55, 0x21, 0xe7, 0xe0, 0xa1, 0x4c, 0x7a, 0xd5, 0xb6, 0x5a, 0x6a, - 0xee, 0x6c, 0x6e, 0x41, 0x21, 0x67, 0xc6, 0x32, 0xb7, 0x75, 0xbd, 0xa5, 0xe6, 0xe9, 0x10, 0x65, - 0x32, 0xc4, 0x04, 0x64, 0xec, 0x85, 0xf2, 0x87, 0x0a, 0x9c, 0x66, 0xd5, 0x14, 0xb3, 0x39, 0xae, - 0xea, 0x79, 0x58, 0xe7, 0xf8, 0x93, 0x93, 0x2a, 0x7a, 0x0a, 0xd4, 0x54, 0x2e, 0xab, 0xe6, 0xc3, - 0xb0, 0x96, 0x4a, 0xc5, 0x7a, 0xe4, 0xe8, 0x5a, 0x4d, 0x25, 0x6f, 0xea, 0x8e, 0xeb, 0xe9, 0xb5, - 0x9a, 0x65, 0xbb, 0xac, 0x22, 0xf9, 0x72, 0x09, 0xd6, 0x2a, 0xfe, 0x20, 0xa2, 0x3a, 0x48, 0x38, - 0x0c, 0x7a, 0x21, 0x56, 0x61, 0x19, 0x16, 0xf5, 0x37, 0x5c, 0xdd, 0x74, 0x0c, 0xcb, 0x54, 0x4f, - 0x94, 0xcf, 0x67, 0x68, 0xc4, 0xaa, 0x71, 0x9c, 0xba, 0x7a, 0xa2, 0xdc, 0x81, 0x65, 0xf1, 0x24, - 0x96, 0xcd, 0x8a, 0x0d, 0x38, 0x2b, 0xe6, 0x1a, 0xae, 0xdf, 0x6c, 0x13, 0xd6, 0xe1, 0xd4, 0x78, - 0xbe, 0xee, 0xaa, 0x0a, 0x1d, 0x85, 0x4c, 0x0e, 0x4d, 0xcf, 0x95, 0x7f, 0xaf, 0xc0, 0x3a, 0x0f, - 0x97, 0xc9, 0xed, 0x11, 0x0c, 0xf2, 0x1a, 0xe1, 0xe3, 0xca, 0x70, 0xd9, 0xb5, 0xdb, 0x8e, 0xab, - 0x57, 0xbd, 0xaa, 0xbe, 0x63, 0x54, 0x74, 0x9c, 0x2e, 0x86, 0xad, 0x37, 0x75, 0xd3, 0xcd, 0x7c, - 0xfa, 0x49, 0x78, 0x7c, 0x06, 0xad, 0x69, 0xb9, 0xe2, 0x3f, 0x5d, 0x25, 0x8f, 0xc3, 0xb7, 0x67, - 0x10, 0xc7, 0x84, 0xb9, 0xf2, 0xdb, 0xb0, 0x94, 0x0a, 0xe3, 0x71, 0x06, 0x1e, 0x92, 0xff, 0xb7, - 0xfc, 0x70, 0x3f, 0x08, 0x0f, 0xd4, 0x13, 0xd9, 0x0c, 0x7b, 0x14, 0x86, 0x34, 0x03, 0x17, 0xa4, - 0x9c, 0xe1, 0xfa, 0x83, 0xc3, 0x20, 0xec, 0x44, 0xfe, 0xbe, 0x9a, 0x2b, 0x5f, 0x85, 0xe5, 0x14, - 0xce, 0x20, 0xed, 0xf9, 0x86, 0xc5, 0xf7, 0xab, 0xa6, 0x5e, 0x35, 0xda, 0x4d, 0x75, 0x8e, 0x2e, - 0xc5, 0xba, 0xb1, 0x55, 0x57, 0xa1, 0xfc, 0xb1, 0x42, 0x25, 0x66, 0xec, 0x9f, 0x66, 0x4d, 0x13, - 0x63, 0x45, 0xe7, 0x09, 0x83, 0x24, 0xd5, 0x1d, 0x87, 0x99, 0x52, 0xcf, 0xc3, 0x3a, 0xff, 0xe3, - 0x69, 0x66, 0xd5, 0xab, 0x6b, 0x76, 0x75, 0x57, 0xb3, 0xe9, 0xe4, 0xb9, 0xa9, 0xe6, 0x70, 0x45, - 0x48, 0x29, 0x9e, 0x6b, 0xb5, 0x2b, 0x75, 0x35, 0x4f, 0x27, 0x60, 0x2a, 0xbd, 0x65, 0x98, 0x6a, - 0x01, 0xd7, 0xd7, 0x18, 0x35, 0x16, 0x4b, 0xf3, 0xe7, 0xca, 0xf7, 0x14, 0x38, 0xe3, 0x04, 0x07, - 0x61, 0x27, 0x1a, 0x0d, 0x7c, 0xad, 0x7b, 0xd0, 0x1b, 0x04, 0xd1, 0xed, 0x43, 0x67, 0x14, 0x44, - 0x3e, 0xb9, 0x02, 0x8f, 0x39, 0xc6, 0x96, 0xa9, 0xb9, 0x74, 0x7d, 0x68, 0x8d, 0x2d, 0xcb, 0x36, - 0xdc, 0x7a, 0xd3, 0x73, 0xda, 0xc6, 0xd8, 0xd4, 0xb9, 0x04, 0x17, 0xa7, 0x93, 0x36, 0xf4, 0x2d, - 0xad, 0x72, 0x53, 0x55, 0x66, 0x17, 0xb8, 0xa9, 0x35, 0x34, 0xb3, 0xa2, 0x57, 0xbd, 0x9d, 0x6b, - 0x6a, 0x8e, 0x3c, 0x06, 0x8f, 0x4e, 0x27, 0xad, 0x19, 0x2d, 0x87, 0x92, 0xe5, 0x67, 0x7f, 0xb7, - 0xee, 0x34, 0x29, 0x55, 0xa1, 0x1c, 0x80, 0x9a, 0xf5, 0xa0, 0x1e, 0x33, 0xdc, 0xdb, 0x6d, 0xd3, - 0x64, 0xbb, 0xe4, 0x2a, 0x14, 0x2d, 0xb7, 0xae, 0xdb, 0x1c, 0x19, 0x17, 0xa1, 0x70, 0xdb, 0xa6, - 0xd6, 0x76, 0xeb, 0x96, 0x6d, 0xbc, 0x89, 0xdb, 0xe5, 0x3a, 0x9c, 0x72, 0x1a, 0x5a, 0x65, 0x1b, - 0x67, 0xa6, 0x61, 0x7a, 0x95, 0xba, 0x66, 0x9a, 0x7a, 0x43, 0x85, 0xf2, 0x6f, 0x15, 0x66, 0x41, - 0x9f, 0xe4, 0x6a, 0x45, 0x9e, 0x82, 0x27, 0xac, 0x6d, 0x57, 0xf3, 0x5a, 0x8d, 0xf6, 0x96, 0x61, - 0x7a, 0xce, 0x4d, 0xb3, 0x22, 0x0e, 0xd2, 0xca, 0xf8, 0x8e, 0xf2, 0x04, 0x5c, 0x9a, 0x49, 0x9d, - 0x60, 0xd8, 0x5e, 0x86, 0xd2, 0x4c, 0x4a, 0xde, 0x90, 0xf2, 0x9f, 0x15, 0x38, 0x37, 0xc3, 0xf2, - 0x48, 0x9e, 0x86, 0x2b, 0x75, 0x5d, 0xab, 0x36, 0x74, 0xc7, 0xf1, 0x68, 0x7b, 0x75, 0xd3, 0xe5, - 0x06, 0xfe, 0x89, 0xfb, 0xc5, 0x15, 0x78, 0x6c, 0x36, 0x79, 0x72, 0xf2, 0x3c, 0x01, 0x97, 0x66, - 0x93, 0xf2, 0x93, 0x28, 0x47, 0x77, 0x8d, 0xd9, 0x94, 0xf1, 0x09, 0x96, 0x2f, 0x7f, 0xa4, 0xc0, - 0xe9, 0xc9, 0x0a, 0x3b, 0xad, 0x9b, 0x61, 0x3a, 0xae, 0xd6, 0x68, 0x78, 0x2d, 0xcd, 0xd6, 0x9a, - 0x9e, 0x6e, 0xda, 0x56, 0xa3, 0x31, 0x69, 0xe7, 0xbe, 0x04, 0x17, 0xa7, 0x93, 0x3a, 0x15, 0xdb, - 0x68, 0xd1, 0x2d, 0xb0, 0x04, 0x1b, 0xd3, 0xa9, 0x74, 0xa3, 0xa2, 0xab, 0xb9, 0xcd, 0x97, 0x3f, - 0xfd, 0xfb, 0xc6, 0x89, 0x4f, 0xef, 0x6d, 0x28, 0x9f, 0xdf, 0xdb, 0x50, 0xfe, 0x76, 0x6f, 0x43, - 0x79, 0xf3, 0xc9, 0x63, 0xc4, 0xfa, 0x7e, 0x67, 0x1e, 0x5f, 0xb4, 0x5c, 0xff, 0x4f, 0x00, 0x00, - 0x00, 0xff, 0xff, 0x9b, 0xa4, 0x11, 0xc6, 0x20, 0x90, 0x01, 0x00, + 0x9d, 0x4d, 0x91, 0x02, 0x21, 0x52, 0x94, 0xad, 0x37, 0x08, 0xe9, 0xb4, 0xf7, 0x5f, 0x42, 0x28, + 0xf9, 0x7e, 0x98, 0x65, 0xdf, 0x4b, 0x08, 0x11, 0xd9, 0x43, 0xe6, 0xc6, 0xb0, 0x96, 0x2f, 0xf6, + 0xd6, 0x89, 0xae, 0xa4, 0x91, 0xf1, 0xdc, 0x82, 0xd1, 0x1b, 0x7a, 0x81, 0x1a, 0x68, 0x9c, 0x5a, + 0x11, 0x26, 0xe5, 0xc1, 0xd6, 0xf3, 0x8c, 0xba, 0x3d, 0xe4, 0xdb, 0x69, 0xf9, 0x2d, 0x70, 0xf9, + 0xe6, 0xeb, 0x81, 0x72, 0x08, 0x22, 0x5f, 0x05, 0x12, 0x06, 0xb2, 0x73, 0x18, 0x95, 0xd9, 0x76, + 0xb9, 0xd9, 0x38, 0x0a, 0x88, 0xf7, 0x64, 0xb1, 0xba, 0x48, 0x92, 0xc4, 0x84, 0xc2, 0xac, 0xe8, + 0x34, 0x83, 0xca, 0x67, 0x3a, 0xfc, 0xb9, 0x49, 0x2d, 0x37, 0x4b, 0x54, 0x12, 0x3d, 0xb6, 0xa7, + 0xbc, 0xf5, 0xa1, 0x99, 0x9c, 0xd2, 0xd8, 0x91, 0x1b, 0x30, 0x8a, 0xd1, 0x6a, 0x2b, 0xd2, 0xc1, + 0x4a, 0x38, 0x7b, 0x60, 0x5c, 0x9b, 0xb5, 0xa7, 0xbb, 0x49, 0x45, 0xa8, 0xec, 0x38, 0x50, 0xf1, + 0x0e, 0xcc, 0x6e, 0x1b, 0x8d, 0xbb, 0xc2, 0xde, 0xd1, 0xf0, 0x0e, 0x2c, 0xaf, 0xab, 0x87, 0x33, + 0x22, 0x12, 0xf9, 0x26, 0x8c, 0xdd, 0xb5, 0x1f, 0x49, 0xdb, 0xae, 0x30, 0xe0, 0x0e, 0xf4, 0x4c, + 0x7c, 0xcb, 0x7e, 0x64, 0x35, 0xba, 0xf1, 0xcc, 0x86, 0xfc, 0x99, 0x78, 0x85, 0x25, 0xf9, 0x08, + 0x40, 0xb1, 0x38, 0x93, 0x23, 0x2b, 0x78, 0x41, 0x66, 0x1c, 0x4a, 0xb5, 0x44, 0x23, 0x7f, 0x85, + 0x61, 0x4c, 0x73, 0x98, 0xfd, 0xec, 0x34, 0x87, 0x13, 0x9f, 0x9d, 0xe6, 0x30, 0xbf, 0x0d, 0xa7, + 0x7b, 0x7e, 0x3a, 0x29, 0x89, 0x20, 0xaf, 0xea, 0x89, 0x20, 0x4f, 0xf7, 0xda, 0x62, 0x7d, 0x3d, + 0x41, 0xf3, 0x4c, 0x71, 0xb6, 0xb7, 0x76, 0xf2, 0xdd, 0x6c, 0x6c, 0xcb, 0x15, 0x07, 0x0b, 0x9e, + 0xd0, 0xbf, 0x97, 0x4e, 0x92, 0xc5, 0xc7, 0xd7, 0xf8, 0xa6, 0x9c, 0x8d, 0x0e, 0x34, 0xb1, 0xf7, + 0x6a, 0xf9, 0xf6, 0xfc, 0xa4, 0xbb, 0xef, 0xdb, 0x30, 0xc9, 0x9f, 0x5d, 0xba, 0x43, 0x0f, 0xf6, + 0x5d, 0xaf, 0x21, 0x1f, 0x25, 0x45, 0x1d, 0x3c, 0xf1, 0xd8, 0x61, 0x0c, 0x97, 0x54, 0x64, 0x00, + 0xd4, 0x10, 0xd6, 0x7e, 0x3a, 0x55, 0x8a, 0x31, 0x84, 0x7e, 0xb1, 0x51, 0xe4, 0x8d, 0x50, 0x51, + 0xa3, 0x9e, 0x9a, 0xa6, 0xd9, 0x93, 0xc0, 0x14, 0x7d, 0x8d, 0x7a, 0xc6, 0x1f, 0xe4, 0x80, 0xf0, + 0x9a, 0x96, 0xec, 0x8e, 0x8d, 0xe1, 0x81, 0x0e, 0xa6, 0xb9, 0x28, 0x0a, 0x1c, 0x7b, 0xbb, 0x49, + 0xd5, 0x1c, 0x31, 0xc2, 0xa1, 0x35, 0x2c, 0xb3, 0xe2, 0x07, 0x9d, 0x04, 0x61, 0x0f, 0x51, 0x97, + 0x7d, 0x12, 0x51, 0xf7, 0x4d, 0x38, 0x53, 0xea, 0xe0, 0xfb, 0x6d, 0xb2, 0x96, 0x9b, 0xae, 0x27, + 0x85, 0x94, 0x16, 0x78, 0x62, 0x87, 0x68, 0x89, 0x96, 0xf6, 0x63, 0xa1, 0xe8, 0x29, 0x6c, 0x5d, + 0x76, 0x02, 0x35, 0x90, 0x59, 0xea, 0x29, 0x1d, 0x2c, 0x49, 0xd1, 0x53, 0x38, 0x89, 0xe4, 0xe1, + 0x78, 0x52, 0x4f, 0xc1, 0x87, 0x09, 0x22, 0x1e, 0x8e, 0x47, 0x7b, 0xe8, 0x3a, 0x21, 0x09, 0x79, + 0x1b, 0xc6, 0x4a, 0xdd, 0xc0, 0x15, 0x8c, 0x85, 0x27, 0x76, 0xe4, 0x33, 0x2d, 0x9a, 0xa2, 0x1d, + 0x7d, 0x22, 0x74, 0xe3, 0x8f, 0x73, 0x70, 0x3a, 0x39, 0xbd, 0xa2, 0x34, 0xfc, 0x3e, 0x32, 0x47, + 0x7c, 0x1f, 0x69, 0xab, 0x81, 0x5f, 0x16, 0x3c, 0xb5, 0xd5, 0xc0, 0x9f, 0x81, 0xfb, 0x94, 0xab, + 0xa1, 0x06, 0x63, 0xea, 0x7e, 0x97, 0xff, 0xb4, 0xfb, 0x9d, 0xca, 0x85, 0x1d, 0xea, 0x79, 0xfc, + 0xf6, 0x50, 0x74, 0x75, 0x14, 0x0f, 0xdd, 0xe6, 0x18, 0xe4, 0x5f, 0x81, 0xf3, 0x5c, 0x26, 0xc5, + 0x3b, 0x5b, 0x3e, 0x90, 0x1c, 0xc5, 0xc4, 0x2d, 0x3e, 0x3e, 0x5c, 0xb8, 0xc2, 0x4d, 0x25, 0x56, + 0x62, 0xd8, 0xac, 0xed, 0x03, 0x4b, 0xb6, 0x4c, 0xa9, 0xe4, 0x48, 0xde, 0xf8, 0xf6, 0x9b, 0xf2, + 0x2e, 0xd7, 0xeb, 0x69, 0x21, 0x2a, 0x3c, 0xd7, 0x29, 0x07, 0xeb, 0xd1, 0x29, 0xd2, 0x1c, 0x96, + 0x4d, 0x35, 0x87, 0x49, 0x7b, 0x4a, 0x2e, 0xd5, 0x9e, 0x52, 0x81, 0xa9, 0x5a, 0x77, 0x5b, 0xd6, + 0x8d, 0x88, 0x79, 0x2d, 0xca, 0x2e, 0xad, 0x43, 0x71, 0x12, 0xe3, 0x47, 0xb3, 0x30, 0xbe, 0xd1, + 0xec, 0xee, 0x3a, 0xed, 0x8a, 0x1d, 0xd8, 0xcf, 0xac, 0x85, 0xee, 0x4d, 0xcd, 0x42, 0x17, 0x46, + 0x62, 0x85, 0x1d, 0x1b, 0xc8, 0x3c, 0xf7, 0xd3, 0x19, 0x98, 0x8a, 0x48, 0xf8, 0x3e, 0xbb, 0x02, + 0x79, 0xf6, 0x43, 0x9c, 0x5b, 0xcf, 0x27, 0x18, 0xf3, 0xc7, 0x60, 0xc2, 0xbf, 0x84, 0xcd, 0x4c, + 0x7f, 0x69, 0x01, 0x39, 0xcc, 0x7f, 0x09, 0x46, 0x23, 0xb6, 0xc7, 0x79, 0x04, 0xe6, 0x57, 0x33, + 0x50, 0x8c, 0xf7, 0x84, 0xdc, 0x81, 0x11, 0xc6, 0xc9, 0xa1, 0xf2, 0x48, 0xfd, 0x62, 0x8f, 0x3e, + 0x5f, 0x11, 0x68, 0xbc, 0x79, 0x38, 0xf8, 0x94, 0x43, 0x4c, 0xc9, 0x61, 0xde, 0x84, 0x71, 0x15, + 0x2b, 0xa5, 0x75, 0xaf, 0xe9, 0xca, 0xc5, 0xc9, 0xf4, 0x71, 0xd0, 0x9e, 0xae, 0xd1, 0x5a, 0x2d, + 0xf4, 0x86, 0x8b, 0xda, 0xe2, 0xc2, 0xb1, 0x8a, 0xad, 0x1b, 0xbe, 0xcc, 0x16, 0xa3, 0xf4, 0xcb, + 0xea, 0x3a, 0x4b, 0x59, 0xd0, 0x21, 0x1e, 0x79, 0x0d, 0x86, 0x79, 0x7d, 0xea, 0x13, 0x0e, 0x1d, + 0x84, 0xa8, 0x2a, 0x2e, 0xc7, 0x31, 0xfe, 0x56, 0x0e, 0x4e, 0x46, 0xcd, 0xbb, 0xd7, 0x69, 0xd8, + 0x01, 0xdd, 0xb0, 0x3d, 0xbb, 0xe5, 0x1f, 0xf1, 0x05, 0x5c, 0x4a, 0x34, 0x0d, 0x53, 0xfa, 0xcb, + 0xa6, 0x29, 0x0d, 0x32, 0x62, 0x0d, 0x42, 0xf3, 0x25, 0x6f, 0x90, 0x6c, 0x06, 0xb9, 0x03, 0xb9, + 0x1a, 0x0d, 0x84, 0xd8, 0xbc, 0x98, 0x18, 0x55, 0xb5, 0x5d, 0x57, 0x6a, 0x34, 0xe0, 0x93, 0xc8, + 0xf3, 0x8a, 0x50, 0x2d, 0xaf, 0x63, 0x8d, 0x06, 0x64, 0x0b, 0x86, 0x97, 0x1f, 0x75, 0x68, 0x3d, + 0x10, 0x4f, 0x18, 0x5d, 0xee, 0xcf, 0x8f, 0xe3, 0x2a, 0x2f, 0x18, 0x51, 0x04, 0xa8, 0x83, 0xc5, + 0x51, 0xe6, 0x6f, 0x40, 0x41, 0x56, 0x7e, 0x9c, 0x95, 0x3b, 0xff, 0x26, 0x8c, 0x29, 0x95, 0x1c, + 0x6b, 0xd1, 0xff, 0x3c, 0x93, 0xab, 0x6e, 0x53, 0xbe, 0x7a, 0xb4, 0x9c, 0x50, 0xf3, 0x32, 0x51, + 0x3c, 0x30, 0x57, 0xf3, 0xac, 0x07, 0xa2, 0xa8, 0x8f, 0xbe, 0x57, 0x85, 0xa9, 0xda, 0x03, 0xa7, + 0x13, 0xa5, 0xd7, 0xd3, 0x36, 0x53, 0xcc, 0x47, 0x2f, 0xce, 0xdc, 0xf1, 0xcd, 0x34, 0x4e, 0x67, + 0xfc, 0x59, 0x06, 0x86, 0xd9, 0x5f, 0xf7, 0x6f, 0x3c, 0xa3, 0x22, 0xf3, 0xba, 0x26, 0x32, 0xa7, + 0x95, 0x0c, 0xb7, 0x28, 0x38, 0x6e, 0x1c, 0x21, 0x2c, 0x0f, 0xc5, 0x04, 0x71, 0x64, 0x72, 0x0b, + 0x46, 0x84, 0x57, 0x8f, 0x70, 0xbf, 0x56, 0x53, 0xe6, 0x4a, 0x7f, 0x9f, 0xf0, 0x70, 0xee, 0x76, + 0xe2, 0xd6, 0x0c, 0x49, 0xcd, 0x54, 0x72, 0x99, 0xe8, 0x50, 0x7b, 0x2b, 0xcf, 0xc5, 0xd8, 0x36, + 0x9e, 0xf0, 0x55, 0x79, 0xdd, 0xb2, 0x47, 0xd2, 0x80, 0x92, 0xb8, 0xc8, 0xc8, 0xf5, 0x63, 0x72, + 0x52, 0x3e, 0x25, 0x96, 0x7a, 0xc7, 0xf1, 0x87, 0x33, 0x3c, 0x4d, 0xaa, 0x6c, 0xd8, 0x3b, 0x30, + 0x7e, 0xd3, 0xf5, 0xf6, 0x6d, 0x8f, 0x27, 0xbf, 0x13, 0x9e, 0x03, 0xec, 0xe8, 0x38, 0xb1, 0xc3, + 0xe1, 0x3c, 0x7d, 0xde, 0x27, 0x87, 0x0b, 0xf9, 0xb2, 0xeb, 0x36, 0x4d, 0x0d, 0x9d, 0xac, 0xc3, + 0xc4, 0x5d, 0xfb, 0x91, 0x72, 0xe8, 0xe5, 0xc1, 0x2a, 0x97, 0xd9, 0x02, 0x66, 0xa7, 0xe6, 0xa3, + 0x5d, 0xac, 0x74, 0x7a, 0xe2, 0xc0, 0xe4, 0x86, 0xeb, 0x05, 0xa2, 0x12, 0xa7, 0xbd, 0x2b, 0x3a, + 0x9b, 0x74, 0x12, 0xbb, 0x9a, 0xea, 0x24, 0x76, 0xba, 0xe3, 0x7a, 0x81, 0xb5, 0x13, 0x92, 0x6b, + 0x09, 0x79, 0x34, 0xc6, 0xe4, 0x1d, 0x98, 0x56, 0x12, 0x8e, 0xdd, 0x74, 0xbd, 0x96, 0x2d, 0x95, + 0x72, 0xb4, 0x03, 0xa3, 0xbf, 0xc9, 0x0e, 0x82, 0xcd, 0x24, 0x26, 0xf9, 0x30, 0x2d, 0xfc, 0x67, + 0x28, 0xf2, 0x32, 0x4b, 0x09, 0xff, 0xe9, 0xe5, 0x65, 0x96, 0x0c, 0x04, 0xda, 0xed, 0xe7, 0x85, + 0x5a, 0x28, 0x5f, 0x13, 0xc7, 0xef, 0xa3, 0xbd, 0x4c, 0xc3, 0x79, 0xeb, 0xe1, 0x6d, 0xba, 0x08, + 0xb9, 0xf2, 0xc6, 0x4d, 0xbc, 0xbd, 0x90, 0x8e, 0x36, 0xed, 0x3d, 0xbb, 0x5d, 0x47, 0x65, 0x59, + 0x78, 0x7e, 0xab, 0x12, 0xb9, 0xbc, 0x71, 0x93, 0xd8, 0x30, 0xb3, 0x41, 0xbd, 0x96, 0x13, 0x7c, + 0xed, 0xda, 0x35, 0x65, 0xa2, 0x0a, 0xd8, 0xb4, 0xab, 0xa2, 0x69, 0x0b, 0x1d, 0x44, 0xb1, 0x1e, + 0x5d, 0xbb, 0x96, 0x3a, 0x1d, 0x61, 0xc3, 0xd2, 0x78, 0x31, 0xc9, 0x78, 0xd7, 0x7e, 0x14, 0x39, + 0xec, 0xfb, 0x22, 0x90, 0xf2, 0x9c, 0x5c, 0x58, 0x91, 0xb3, 0xbf, 0x26, 0x19, 0x75, 0x22, 0x76, + 0xd6, 0x89, 0x96, 0x97, 0x2f, 0x42, 0x50, 0xe6, 0xa5, 0x49, 0x47, 0x46, 0xdb, 0xaa, 0x0a, 0xbb, + 0x82, 0x4e, 0xee, 0x85, 0x27, 0x36, 0x7e, 0xe2, 0x11, 0x0f, 0x65, 0x5d, 0x55, 0x4f, 0x6c, 0xdc, + 0x90, 0xa2, 0x75, 0x6b, 0x2a, 0x3c, 0xe6, 0xf3, 0x08, 0x06, 0x53, 0xe7, 0x92, 0x3c, 0x08, 0x8e, + 0x1f, 0xff, 0x20, 0x48, 0x21, 0xbf, 0xea, 0xd6, 0x1f, 0x88, 0x2c, 0x42, 0x5f, 0x65, 0x9f, 0x7b, + 0xd3, 0xad, 0x3f, 0x78, 0x7a, 0xde, 0xb5, 0xc8, 0x9e, 0xac, 0xb1, 0xa6, 0xb2, 0x55, 0x20, 0xc6, + 0x44, 0x78, 0x6c, 0xce, 0x86, 0x27, 0x21, 0xa5, 0x8c, 0x2b, 0x3e, 0x7c, 0xd1, 0xc8, 0xa1, 0x35, + 0x75, 0x72, 0x42, 0xa1, 0x58, 0xa1, 0xfe, 0x83, 0xc0, 0xed, 0x2c, 0x35, 0x9d, 0xce, 0xb6, 0x6b, + 0x7b, 0x0d, 0xb4, 0xdd, 0xa5, 0x7d, 0xdf, 0x2f, 0xa7, 0x7e, 0xdf, 0xd3, 0x0d, 0x4e, 0x6f, 0xd5, + 0x25, 0x03, 0x33, 0xc1, 0x92, 0x7c, 0x08, 0x93, 0x6c, 0x71, 0x2f, 0x3f, 0x0a, 0x68, 0x9b, 0xcf, + 0xfc, 0x34, 0xaa, 0x0e, 0xb3, 0x4a, 0x6a, 0xf1, 0xb0, 0x90, 0xaf, 0x29, 0xfc, 0xd8, 0x69, 0x48, + 0xa0, 0x65, 0x60, 0xd2, 0x58, 0x91, 0x06, 0xcc, 0xdd, 0xb5, 0x1f, 0x29, 0xcf, 0x7b, 0x29, 0x8b, + 0x94, 0xe0, 0x02, 0xc3, 0x17, 0xd9, 0xd9, 0x02, 0x8b, 0x92, 0x7f, 0xf6, 0x58, 0xaf, 0x3d, 0x39, + 0x91, 0xef, 0x85, 0x53, 0xa2, 0x5b, 0x15, 0x7c, 0x6f, 0xc3, 0xf5, 0x0e, 0x6a, 0x7b, 0x36, 0xc6, + 0xea, 0xcc, 0x1c, 0x4f, 0x20, 0xca, 0x01, 0x6b, 0x48, 0x3e, 0x96, 0xcf, 0x19, 0x99, 0xbd, 0x6a, + 0x20, 0x1f, 0xc3, 0x24, 0xbf, 0xb2, 0x59, 0x71, 0xfd, 0x00, 0x0f, 0xf4, 0xb3, 0xc7, 0x73, 0x41, + 0xe7, 0xf7, 0x40, 0x3c, 0x68, 0x23, 0x66, 0x00, 0x88, 0x71, 0x26, 0x6f, 0xc1, 0xd8, 0x86, 0xd3, + 0xe6, 0x39, 0xd2, 0xaa, 0x1b, 0x68, 0x7a, 0x14, 0xfb, 0x4f, 0xc7, 0x69, 0x5b, 0xf2, 0x54, 0xdd, + 0x09, 0xc5, 0x85, 0x8a, 0x4d, 0xb6, 0x60, 0xac, 0x56, 0x5b, 0xb9, 0xe9, 0xb0, 0x0d, 0xb0, 0x73, + 0x30, 0x77, 0xb2, 0x47, 0x2b, 0x2f, 0xa4, 0xb6, 0x72, 0xc2, 0xf7, 0xf7, 0xf0, 0xc9, 0x64, 0xab, + 0xee, 0x76, 0x0e, 0x4c, 0x95, 0x53, 0x8a, 0x5b, 0xf6, 0xa9, 0xa7, 0xec, 0x96, 0x5d, 0x85, 0x29, + 0xc5, 0xc1, 0x12, 0x9d, 0x2b, 0xe7, 0xa2, 0x94, 0x60, 0xaa, 0x1b, 0x76, 0x3c, 0x64, 0x30, 0x4e, + 0x27, 0xfd, 0xb1, 0x4f, 0x1f, 0xd7, 0x1f, 0xdb, 0x81, 0x69, 0x3e, 0x19, 0x62, 0x1d, 0xe0, 0x4c, + 0xcf, 0xf7, 0x18, 0xc3, 0xcb, 0xa9, 0x63, 0x38, 0x23, 0x66, 0x5a, 0x2e, 0x32, 0xbc, 0xa2, 0x4c, + 0x72, 0x25, 0x3b, 0x40, 0x04, 0x50, 0x3c, 0xd8, 0x8c, 0x75, 0x9d, 0xe9, 0x51, 0xd7, 0x8b, 0xa9, + 0x75, 0x4d, 0xca, 0xba, 0xb6, 0x79, 0x35, 0x29, 0x1c, 0x49, 0x5b, 0xd6, 0x23, 0xd7, 0x17, 0x0e, + 0xec, 0x59, 0xcd, 0x0e, 0x9a, 0x44, 0xe0, 0x09, 0x4a, 0xe3, 0x8b, 0x36, 0x3e, 0xee, 0x29, 0x9c, + 0xc9, 0x23, 0x38, 0x99, 0x6c, 0x05, 0xd6, 0x79, 0x0e, 0xeb, 0x3c, 0xa7, 0xd5, 0x19, 0x47, 0xe2, + 0xeb, 0x46, 0xef, 0x56, 0xbc, 0xd6, 0x1e, 0xfc, 0x6f, 0xe7, 0x0b, 0x13, 0xc5, 0xc9, 0x34, 0x4f, + 0xeb, 0xdf, 0xcc, 0xc6, 0x84, 0x36, 0xa9, 0xc2, 0x88, 0x98, 0x0b, 0xa1, 0xc5, 0x26, 0x47, 0xfc, + 0x5c, 0xea, 0x88, 0x8f, 0x88, 0x69, 0x35, 0x25, 0x3d, 0xd9, 0x67, 0xac, 0xd0, 0x25, 0x5e, 0xa8, + 0xfd, 0x1f, 0x71, 0x99, 0x8c, 0x20, 0x6d, 0xf7, 0xa9, 0x1c, 0x3f, 0x20, 0x48, 0x8f, 0x37, 0xc3, + 0x6d, 0x48, 0xd6, 0x46, 0x1e, 0xf0, 0x57, 0x08, 0x72, 0x61, 0x54, 0x89, 0xfe, 0xe4, 0xc0, 0x53, + 0xab, 0x90, 0xd5, 0x62, 0xfc, 0x46, 0x06, 0x26, 0x34, 0xa9, 0x4f, 0x6e, 0x28, 0x21, 0x53, 0x51, + 0xc4, 0xaf, 0x86, 0x83, 0x82, 0x20, 0x1e, 0x4c, 0x75, 0x43, 0xf8, 0x4d, 0x67, 0x7b, 0xd3, 0xa5, + 0xbe, 0x5b, 0xde, 0xdf, 0x48, 0x16, 0xbe, 0x6d, 0x94, 0xef, 0xf1, 0xb6, 0xd1, 0xaf, 0x9d, 0x81, + 0x49, 0xfd, 0x58, 0x40, 0x5e, 0x83, 0x61, 0xb4, 0x2d, 0xca, 0x33, 0x26, 0x7f, 0xdd, 0x17, 0x21, + 0xda, 0xeb, 0xbe, 0x08, 0x21, 0x2f, 0x01, 0x84, 0x0e, 0xac, 0xd2, 0xb2, 0x3e, 0xf4, 0xf8, 0x70, + 0x21, 0xf3, 0xba, 0xa9, 0x14, 0x90, 0x6f, 0x00, 0xac, 0xb9, 0x0d, 0x1a, 0xbe, 0xbf, 0xd6, 0xe7, + 0xf6, 0xf8, 0xe5, 0x44, 0x86, 0xee, 0x13, 0x6d, 0xb7, 0x41, 0x93, 0xe9, 0xb8, 0x15, 0x8e, 0xe4, + 0x2b, 0x30, 0x64, 0x76, 0xd9, 0x79, 0x96, 0x9b, 0x12, 0xc6, 0xa4, 0xf4, 0xed, 0x36, 0x69, 0x74, + 0x58, 0xf2, 0xba, 0x71, 0xc7, 0x28, 0x06, 0x20, 0xef, 0xf1, 0xcc, 0xdd, 0x22, 0xd1, 0xd6, 0x50, + 0x74, 0xd7, 0xa0, 0xec, 0xca, 0x89, 0x54, 0x5b, 0x0a, 0x09, 0x59, 0x87, 0x11, 0xd5, 0x48, 0xae, + 0xc4, 0xde, 0xaa, 0x17, 0x29, 0xca, 0xc9, 0x4b, 0x3c, 0xdc, 0x16, 0xb7, 0x9f, 0x4b, 0x2e, 0xe4, + 0x6d, 0x18, 0x65, 0xec, 0xd9, 0x27, 0xec, 0x0b, 0x8d, 0x1b, 0x6f, 0x14, 0x94, 0x06, 0x31, 0x09, + 0xa0, 0xa5, 0xc3, 0x0a, 0x09, 0xc8, 0x87, 0xf8, 0x36, 0x99, 0x18, 0xea, 0xbe, 0x5e, 0x05, 0x17, + 0x13, 0x43, 0x8d, 0x8f, 0x95, 0x25, 0x9f, 0xad, 0x0d, 0xf9, 0x91, 0xdd, 0x30, 0x4d, 0xd3, 0x20, + 0xd9, 0xd6, 0x5f, 0x49, 0x54, 0x30, 0x27, 0x33, 0x0f, 0x25, 0xdf, 0xd1, 0xd3, 0xf8, 0x92, 0x0e, + 0x14, 0x23, 0x85, 0x47, 0xd4, 0x05, 0xfd, 0xea, 0x7a, 0x3d, 0x51, 0x97, 0x3a, 0x81, 0x89, 0xea, + 0x12, 0xdc, 0x49, 0x03, 0x26, 0xa5, 0xf0, 0x14, 0xf5, 0x8d, 0xf5, 0xab, 0xef, 0xa5, 0x44, 0x7d, + 0x33, 0x8d, 0xed, 0x64, 0x3d, 0x31, 0x9e, 0xe4, 0x6d, 0x98, 0x90, 0x10, 0xfc, 0x3e, 0xc4, 0xfb, + 0xb8, 0x68, 0x15, 0x69, 0x6c, 0xa3, 0xcb, 0xbc, 0xfe, 0xba, 0xa0, 0x8a, 0xac, 0x52, 0xf3, 0xd5, + 0x31, 0xa1, 0x51, 0xc7, 0x57, 0x85, 0x8e, 0x4c, 0x3e, 0x80, 0xb1, 0x6a, 0x8b, 0x75, 0xc4, 0x6d, + 0xdb, 0x01, 0x15, 0x71, 0x59, 0xd2, 0x43, 0x42, 0x29, 0x51, 0x96, 0x2a, 0x7f, 0xaf, 0x2f, 0x2a, + 0xd2, 0xde, 0xeb, 0x8b, 0xc0, 0x6c, 0xf0, 0xf8, 0xad, 0x88, 0x58, 0xc3, 0x32, 0x66, 0xeb, 0x5c, + 0x8a, 0x97, 0x82, 0xc2, 0x5e, 0x24, 0xb4, 0x63, 0x50, 0x79, 0x2b, 0x11, 0x4b, 0x68, 0xa7, 0xf2, + 0x24, 0xef, 0xc0, 0x98, 0x78, 0x88, 0xa2, 0x64, 0xae, 0xf9, 0x73, 0x45, 0xec, 0x3c, 0x46, 0x9a, + 0xcb, 0x37, 0x2b, 0x2c, 0xdb, 0x8b, 0xb9, 0xe3, 0x45, 0xf8, 0xe4, 0x6b, 0x30, 0xbb, 0xe5, 0xb4, + 0x1b, 0xee, 0xbe, 0x2f, 0xb6, 0x29, 0x21, 0xe8, 0xa6, 0xa3, 0x60, 0x98, 0x7d, 0x5e, 0x1e, 0xea, + 0x29, 0x09, 0xc1, 0x97, 0xca, 0x81, 0xfc, 0xa5, 0x04, 0x67, 0xbe, 0x82, 0x48, 0xbf, 0x15, 0xb4, + 0x98, 0x58, 0x41, 0xc9, 0xea, 0xe3, 0xcb, 0x29, 0xb5, 0x1a, 0xe2, 0x02, 0xd1, 0xf7, 0xf7, 0xdb, + 0xae, 0xd3, 0x9e, 0x9b, 0x41, 0x59, 0x78, 0x26, 0x1e, 0xdb, 0x8d, 0x78, 0x1b, 0x6e, 0xd3, 0xa9, + 0x1f, 0xf0, 0xb7, 0xf1, 0xe3, 0xfa, 0xe8, 0xc7, 0xae, 0x66, 0x33, 0x4e, 0x61, 0x4d, 0x3e, 0x80, + 0x71, 0xf6, 0x7f, 0x78, 0x60, 0x9e, 0xd5, 0xfc, 0xda, 0x14, 0x4c, 0x51, 0x0f, 0xce, 0x11, 0xbe, + 0x94, 0x91, 0x72, 0x96, 0xd6, 0x58, 0x91, 0x37, 0x01, 0x98, 0xe6, 0x24, 0xc4, 0xf1, 0x89, 0x28, + 0x7f, 0x20, 0xea, 0x5b, 0x49, 0x41, 0x1c, 0x21, 0xb3, 0x53, 0x3c, 0xfb, 0x55, 0xeb, 0x36, 0x5c, + 0xf6, 0x6d, 0x9c, 0x44, 0x5a, 0x1e, 0xee, 0xc6, 0x68, 0x7d, 0x0e, 0xd7, 0xc2, 0xdd, 0x22, 0x74, + 0xb2, 0x02, 0x53, 0x98, 0xe7, 0xb1, 0xda, 0xa0, 0xed, 0x00, 0x6f, 0x2b, 0xe7, 0x4e, 0x29, 0xb7, + 0xb9, 0xac, 0xc8, 0x72, 0xc2, 0x32, 0x55, 0xcf, 0x8e, 0x91, 0x11, 0x1f, 0x66, 0x22, 0xe9, 0x12, + 0xdd, 0x0d, 0xcf, 0xe1, 0x20, 0x49, 0xed, 0x32, 0x89, 0xc1, 0xe5, 0x31, 0x9b, 0x11, 0x45, 0x70, + 0x49, 0xcb, 0xba, 0x5a, 0x61, 0x1a, 0x77, 0x62, 0x02, 0xb9, 0xb5, 0xb4, 0x11, 0x4f, 0x84, 0x78, + 0x1a, 0x7b, 0x80, 0xd3, 0xbc, 0x5b, 0x8f, 0x5e, 0x86, 0x4c, 0x49, 0x86, 0x98, 0x42, 0x4d, 0xbe, + 0x0d, 0x27, 0xa4, 0x04, 0x11, 0x45, 0x62, 0x5d, 0xcf, 0x1f, 0x53, 0x12, 0x37, 0xb6, 0xc3, 0xaa, + 0x13, 0x4b, 0x3a, 0xbd, 0x0a, 0x62, 0xc3, 0x18, 0x4e, 0xab, 0xa8, 0xf1, 0x4c, 0xbf, 0x1a, 0x2f, + 0x25, 0x6a, 0x3c, 0x89, 0x0b, 0x25, 0x59, 0x99, 0xca, 0x93, 0x94, 0x61, 0x42, 0x7c, 0x47, 0x62, + 0xb5, 0x9d, 0xc5, 0xd1, 0x42, 0x03, 0x8b, 0xfc, 0x02, 0x13, 0x0b, 0x4e, 0x27, 0x51, 0x25, 0x32, + 0xb7, 0xa8, 0x9f, 0xd3, 0x24, 0x72, 0xdc, 0x90, 0xae, 0x23, 0x33, 0x89, 0x14, 0x69, 0x31, 0xcb, + 0x8f, 0x3a, 0x9e, 0x30, 0x9f, 0x3c, 0x1f, 0xbd, 0x0f, 0xa0, 0x28, 0x3f, 0x16, 0x0d, 0x31, 0x54, + 0x91, 0x90, 0xc6, 0x81, 0xdc, 0x83, 0x99, 0x70, 0xd7, 0x56, 0x18, 0x2f, 0x44, 0xef, 0x2c, 0x44, + 0x5b, 0x7d, 0x3a, 0xdf, 0x34, 0x7a, 0x62, 0xc3, 0x29, 0x6d, 0x9f, 0x56, 0x58, 0x9f, 0x47, 0xd6, + 0xf8, 0x12, 0xa9, 0xbe, 0xc9, 0xa7, 0xb3, 0xef, 0xc5, 0x87, 0x7c, 0x0c, 0xf3, 0xf1, 0xbd, 0x59, + 0xa9, 0xe5, 0x05, 0xac, 0xe5, 0x95, 0xc7, 0x87, 0x0b, 0x17, 0x13, 0xdb, 0x7b, 0x7a, 0x45, 0x7d, + 0xb8, 0x91, 0x6f, 0xc0, 0x9c, 0xbe, 0x3f, 0x2b, 0x35, 0x19, 0x58, 0x13, 0x7e, 0x3a, 0xe1, 0xc6, + 0x9e, 0x5e, 0x43, 0x4f, 0x1e, 0x24, 0x80, 0x85, 0xd4, 0xd5, 0xad, 0x54, 0x73, 0x21, 0xea, 0x50, + 0xe2, 0x2b, 0x49, 0xaf, 0xee, 0x28, 0x96, 0x64, 0x1f, 0x9e, 0x4f, 0xdb, 0x26, 0x94, 0x4a, 0x5f, + 0x0c, 0x0d, 0x94, 0xaf, 0xa6, 0x6f, 0x39, 0xe9, 0x35, 0x1f, 0xc1, 0x96, 0x7c, 0x08, 0x27, 0x94, + 0xef, 0x4b, 0xa9, 0xef, 0x25, 0xac, 0x0f, 0x43, 0x59, 0xd5, 0x0f, 0x33, 0xbd, 0x96, 0x74, 0x1e, + 0xa4, 0x05, 0x33, 0xb2, 0xe3, 0x68, 0x09, 0x16, 0x5b, 0xcf, 0x45, 0x4d, 0xaa, 0x26, 0x31, 0x94, + 0x27, 0x9c, 0xb7, 0xad, 0x4e, 0x44, 0xa8, 0xae, 0xf4, 0x14, 0xbe, 0x64, 0x05, 0x86, 0x6b, 0x1b, + 0xd5, 0x9b, 0x37, 0x97, 0xe7, 0x5e, 0xc6, 0x1a, 0x64, 0xdc, 0x0b, 0x07, 0x6a, 0x87, 0x26, 0xe1, + 0x6e, 0xd5, 0x71, 0x76, 0x76, 0xb4, 0xf0, 0x22, 0x8e, 0x7a, 0x3b, 0x5f, 0xb8, 0x54, 0xbc, 0x7c, + 0x3b, 0x5f, 0xb8, 0x5c, 0x7c, 0xc5, 0x3c, 0x9b, 0xfe, 0xfa, 0x2e, 0xef, 0xac, 0x79, 0xb1, 0x5f, + 0x69, 0x34, 0x14, 0xc6, 0xcf, 0x67, 0x60, 0x26, 0xa5, 0x1d, 0xe4, 0x22, 0xe4, 0xf1, 0xe1, 0x02, + 0xe5, 0x82, 0x39, 0xf6, 0x60, 0x01, 0x96, 0x93, 0x2f, 0xc0, 0x48, 0x65, 0xad, 0x56, 0x2b, 0xad, + 0xc9, 0x23, 0x1b, 0x17, 0x57, 0x6d, 0xdf, 0xf2, 0x6d, 0xfd, 0x5e, 0x4a, 0xa0, 0x91, 0xd7, 0x61, + 0xb8, 0xba, 0x81, 0x04, 0xdc, 0xc3, 0x09, 0x8f, 0x30, 0x4e, 0x27, 0x8e, 0x2f, 0x90, 0x8c, 0x1f, + 0xcb, 0x00, 0x49, 0x0e, 0x2a, 0xb9, 0x06, 0x63, 0xea, 0xd4, 0xf1, 0x03, 0x26, 0xde, 0xa1, 0x28, + 0x13, 0x63, 0xaa, 0x38, 0xa4, 0x02, 0x43, 0xf8, 0xd0, 0x52, 0x78, 0x21, 0x96, 0xba, 0x01, 0x9c, + 0x4a, 0x6c, 0x00, 0x43, 0xf8, 0x8c, 0x93, 0xc9, 0x89, 0x8d, 0xdf, 0xc9, 0x00, 0x49, 0x6e, 0x9a, + 0x03, 0x5f, 0xc8, 0xbf, 0xa1, 0x44, 0xa8, 0xaa, 0xa9, 0xc9, 0xc3, 0x77, 0x25, 0xd4, 0xc3, 0x52, + 0x14, 0xcb, 0x7a, 0x51, 0x3b, 0x9c, 0xf7, 0x0e, 0x6b, 0xba, 0x0c, 0x43, 0xf7, 0xa9, 0xb7, 0x2d, + 0x9d, 0xf7, 0xd0, 0xe1, 0xe7, 0x21, 0x03, 0xa8, 0x87, 0x55, 0xc4, 0x30, 0xfe, 0x38, 0x03, 0xb3, + 0x69, 0x9a, 0xdc, 0x11, 0xd1, 0x47, 0x46, 0x2c, 0x70, 0x0a, 0x2f, 0xe3, 0xb9, 0x37, 0x50, 0x18, + 0x2e, 0xb5, 0x00, 0x43, 0xac, 0xb3, 0x72, 0x86, 0xd1, 0x58, 0xc0, 0x46, 0xc3, 0x37, 0x39, 0x9c, + 0x21, 0xf0, 0x8c, 0x4a, 0x79, 0x4c, 0x9c, 0x85, 0x08, 0xa8, 0x28, 0x98, 0x1c, 0xce, 0x10, 0xee, + 0xba, 0x8d, 0xf0, 0x8d, 0x51, 0x44, 0x68, 0x31, 0x80, 0xc9, 0xe1, 0xe4, 0x22, 0x8c, 0xac, 0xb7, + 0x57, 0xa9, 0xfd, 0x50, 0x3e, 0x8d, 0x81, 0xce, 0x03, 0x6e, 0xdb, 0x6a, 0x32, 0x98, 0x29, 0x0b, + 0x8d, 0x9f, 0xce, 0xc0, 0x74, 0x42, 0x89, 0x3c, 0x3a, 0xc0, 0xaa, 0x7f, 0xa4, 0xc3, 0x20, 0xfd, + 0xe3, 0xcd, 0xcf, 0xa7, 0x37, 0xdf, 0xf8, 0x3f, 0xf3, 0x70, 0xaa, 0xc7, 0x99, 0x3e, 0x8a, 0xc4, + 0xca, 0x1c, 0x19, 0x89, 0xf5, 0x75, 0x76, 0x86, 0xb6, 0x9d, 0x96, 0xbf, 0xe9, 0x46, 0x2d, 0x8e, + 0x1c, 0xba, 0xb1, 0x4c, 0x3e, 0xb3, 0x2a, 0x3d, 0x7f, 0x4f, 0xf3, 0xa7, 0xae, 0xad, 0xc0, 0x4d, + 0xaa, 0x14, 0x1a, 0xb3, 0x44, 0x2c, 0x54, 0xee, 0xcf, 0x49, 0x2c, 0x94, 0xee, 0x9d, 0x9f, 0x7f, + 0xaa, 0xde, 0xf9, 0xe9, 0x9e, 0x7d, 0x43, 0x4f, 0xe2, 0xe7, 0xb9, 0x04, 0x13, 0xdc, 0x7b, 0xa2, + 0xe4, 0xf3, 0x49, 0x1a, 0x4e, 0x78, 0x5c, 0xd8, 0x7e, 0x72, 0x2e, 0x34, 0x1a, 0xb2, 0xa2, 0x7b, + 0x92, 0x8f, 0xe0, 0xad, 0xcf, 0xc5, 0xde, 0x9e, 0xe2, 0xda, 0x6d, 0xaf, 0x4a, 0x6a, 0xfc, 0x74, + 0x56, 0x0f, 0x94, 0xfa, 0xf3, 0xb8, 0xf2, 0x2e, 0xc3, 0xd0, 0xd6, 0x1e, 0xf5, 0xa4, 0xbc, 0xc3, + 0x86, 0xec, 0x33, 0x80, 0xda, 0x10, 0xc4, 0x20, 0x37, 0x61, 0x72, 0x83, 0xcf, 0x84, 0x1c, 0xde, + 0x7c, 0x74, 0xd4, 0xea, 0x08, 0x83, 0x40, 0xca, 0xf8, 0xc6, 0xa8, 0x8c, 0x5b, 0x70, 0x4e, 0xfb, + 0x20, 0x45, 0x62, 0x07, 0xee, 0xd0, 0xcd, 0x77, 0xc4, 0xc9, 0xc8, 0x85, 0x3d, 0x92, 0x1e, 0x66, + 0x0c, 0x6a, 0xec, 0xc0, 0xf3, 0x7d, 0x19, 0xb1, 0x8d, 0x08, 0x3a, 0xe1, 0xaf, 0x98, 0xd7, 0x59, + 0x5f, 0x52, 0x53, 0xa1, 0x33, 0xbe, 0x17, 0xc6, 0xd5, 0x51, 0x46, 0x99, 0xca, 0x7e, 0x0b, 0xa1, + 0xc6, 0x65, 0x2a, 0x03, 0x98, 0x1c, 0x7e, 0xe4, 0xf3, 0xf4, 0xd1, 0xf4, 0xe7, 0x8e, 0x9a, 0x7e, + 0x56, 0x39, 0x7e, 0xb2, 0x4a, 0xe5, 0xf8, 0x5b, 0xad, 0x1c, 0x33, 0x37, 0x98, 0x1c, 0xfe, 0x54, + 0x2b, 0xff, 0x6d, 0xf9, 0x40, 0x00, 0xfa, 0x8b, 0xcb, 0x33, 0x71, 0xf4, 0xfc, 0xe7, 0x4c, 0xda, + 0x49, 0x37, 0xc2, 0x8c, 0x36, 0xc9, 0xec, 0x51, 0x9b, 0xe4, 0x71, 0x16, 0xe2, 0x55, 0x18, 0x29, + 0x89, 0x3b, 0xd9, 0x7c, 0xa4, 0xd8, 0xd8, 0x89, 0x0b, 0x58, 0x89, 0x65, 0xfc, 0x5c, 0x06, 0x4e, + 0xa4, 0x9a, 0xca, 0x58, 0xad, 0xdc, 0x26, 0xa7, 0x7c, 0x87, 0x71, 0x83, 0x1c, 0xc7, 0x38, 0x4e, + 0xd8, 0xee, 0xe0, 0x7d, 0x31, 0x5e, 0x80, 0xd1, 0xf0, 0xa2, 0x86, 0xcc, 0xca, 0xa9, 0x43, 0x47, + 0x1d, 0x69, 0xef, 0xaf, 0x01, 0xb0, 0x16, 0x3c, 0x55, 0xb7, 0x32, 0xe3, 0xb7, 0xb3, 0xfc, 0xf1, + 0xa8, 0x67, 0x36, 0x93, 0x5e, 0xba, 0x2f, 0x18, 0xeb, 0x52, 0xef, 0xfc, 0x79, 0x64, 0x19, 0x86, + 0x6b, 0x81, 0x1d, 0x74, 0x65, 0xb4, 0xf1, 0x8c, 0x4a, 0x86, 0x05, 0xf7, 0x17, 0xa3, 0x78, 0x53, + 0x1f, 0x21, 0xda, 0xe1, 0x00, 0x21, 0x8a, 0x4b, 0x99, 0x03, 0xe3, 0x2a, 0x2d, 0xf9, 0x00, 0x26, + 0x65, 0x7a, 0x30, 0x1e, 0x82, 0x2d, 0x2e, 0x95, 0xa4, 0x73, 0x82, 0x4c, 0x0f, 0xa6, 0x86, 0x6c, + 0x6b, 0xf8, 0xaa, 0xa4, 0xee, 0xa8, 0xc8, 0xc6, 0x9f, 0x0c, 0xf3, 0x75, 0x20, 0xf2, 0xfc, 0x6d, + 0xc3, 0xe4, 0x7a, 0xb5, 0xb2, 0xa4, 0x18, 0xbe, 0xf4, 0x27, 0x1d, 0x96, 0x1f, 0x05, 0xd4, 0x6b, + 0xdb, 0x4d, 0x81, 0x70, 0x10, 0xed, 0x0d, 0xae, 0xd3, 0xa8, 0xa7, 0x1b, 0xc5, 0x62, 0x1c, 0x59, + 0x1d, 0xfc, 0x70, 0x13, 0xd6, 0x91, 0x1d, 0xb0, 0x0e, 0xdf, 0x6e, 0x35, 0x7b, 0xd4, 0xa1, 0x73, + 0x24, 0x7b, 0x50, 0xbc, 0x85, 0x7a, 0x8c, 0x52, 0x4b, 0xae, 0x7f, 0x2d, 0x17, 0x44, 0x2d, 0x67, + 0xb8, 0x02, 0x94, 0x5e, 0x4f, 0x82, 0x6b, 0xf4, 0x01, 0xe7, 0x8f, 0xfc, 0x80, 0xff, 0x4a, 0x06, + 0x86, 0xb9, 0xa2, 0x24, 0xd6, 0x57, 0x0f, 0x55, 0x6c, 0xeb, 0xe9, 0xa8, 0x62, 0x45, 0x14, 0xe0, + 0xda, 0x4a, 0xe3, 0x65, 0xa4, 0x12, 0x5b, 0xb0, 0xd2, 0x45, 0x11, 0x4d, 0xd8, 0xbc, 0xe4, 0xe8, + 0xf5, 0x4a, 0xaa, 0x51, 0x68, 0xee, 0xc8, 0x91, 0xd1, 0x5f, 0x32, 0x9c, 0x79, 0x44, 0x84, 0xe6, + 0xea, 0x01, 0xb9, 0xab, 0x30, 0x2a, 0x02, 0x7e, 0xcb, 0x07, 0xe2, 0xa2, 0xaa, 0xa8, 0x5d, 0x83, + 0x37, 0xca, 0x07, 0x91, 0x12, 0x28, 0x42, 0x86, 0xad, 0xed, 0x03, 0xed, 0x91, 0x2c, 0x89, 0x48, + 0xd6, 0xf9, 0xe3, 0x31, 0x3c, 0x13, 0xa2, 0x9e, 0xa6, 0x38, 0x84, 0x8b, 0x54, 0x22, 0x32, 0x6a, + 0x30, 0x25, 0xf1, 0x61, 0xc4, 0x83, 0xac, 0x42, 0x51, 0x3c, 0xac, 0xcf, 0xfd, 0x28, 0xaa, 0x15, + 0x1e, 0x54, 0x2a, 0xdc, 0xdf, 0xe4, 0xb3, 0xfc, 0xc2, 0x03, 0x43, 0x8f, 0xe7, 0x48, 0x50, 0xb2, + 0x83, 0x5b, 0x31, 0xbe, 0xfa, 0xc8, 0xdb, 0x30, 0x16, 0x66, 0xa2, 0x0c, 0x23, 0xca, 0xd0, 0x60, + 0x1d, 0xa5, 0xae, 0xd4, 0x62, 0xcb, 0x54, 0x74, 0xb2, 0x08, 0x05, 0xf6, 0x11, 0xc7, 0x9f, 0xe7, + 0xea, 0x0a, 0x98, 0xea, 0x26, 0x2e, 0xf1, 0x48, 0x0d, 0x66, 0xd8, 0x47, 0x53, 0x73, 0xda, 0xbb, + 0x4d, 0xba, 0xea, 0xee, 0xba, 0xdd, 0xe0, 0x9e, 0xb9, 0x2a, 0x84, 0x2b, 0x57, 0x95, 0xed, 0x56, + 0x53, 0x2b, 0xf6, 0xb4, 0xc7, 0x57, 0x53, 0xa8, 0x15, 0x19, 0xf6, 0x87, 0x59, 0x18, 0x53, 0xd6, + 0x13, 0xb9, 0x0c, 0x85, 0xaa, 0xbf, 0xea, 0xd6, 0x1f, 0x84, 0xb9, 0xa6, 0x26, 0x1e, 0x1f, 0x2e, + 0x8c, 0x3a, 0xbe, 0xd5, 0x44, 0xa0, 0x19, 0x16, 0x93, 0x32, 0x4c, 0xf0, 0xbf, 0x64, 0x36, 0xef, + 0x6c, 0xe4, 0xed, 0xc6, 0x91, 0x65, 0x1e, 0x6f, 0x55, 0xae, 0x69, 0x24, 0xe4, 0x23, 0x00, 0x0e, + 0xc0, 0xe8, 0xc4, 0xdc, 0xe0, 0x71, 0x95, 0xa2, 0x82, 0x94, 0xb8, 0x44, 0x85, 0x21, 0xf9, 0x26, + 0xcf, 0x5c, 0x29, 0xd7, 0x7f, 0x7e, 0xf0, 0xc0, 0x50, 0xc6, 0xdf, 0x4a, 0x8f, 0x4f, 0x57, 0x59, + 0x8a, 0xb4, 0x78, 0xf3, 0x26, 0xad, 0xbb, 0x0f, 0xa9, 0x77, 0x50, 0x0a, 0x10, 0x51, 0xc1, 0x30, + 0xfe, 0xc7, 0x8c, 0xf2, 0xd5, 0x90, 0x35, 0x7c, 0x51, 0x8e, 0xaf, 0x08, 0xe1, 0xb3, 0x11, 0x2a, + 0xf3, 0x12, 0x6e, 0xd2, 0x9d, 0xf2, 0x19, 0xe1, 0x6c, 0x39, 0x13, 0xae, 0xab, 0xd8, 0x4b, 0x73, + 0x1c, 0x48, 0xde, 0x87, 0x3c, 0x0e, 0x5d, 0xf6, 0xc8, 0xae, 0xc9, 0xfd, 0x34, 0xcf, 0xc6, 0x0c, + 0x3b, 0x82, 0x94, 0xe4, 0x0b, 0x22, 0xb2, 0x8b, 0x0f, 0xfe, 0xa4, 0xb2, 0x29, 0xb2, 0x76, 0x84, + 0x1b, 0x69, 0x94, 0xa2, 0x40, 0x59, 0x3d, 0xff, 0x46, 0x16, 0x8a, 0xf1, 0x6f, 0x95, 0xbc, 0x07, + 0xe3, 0x72, 0xa7, 0xc3, 0x27, 0x87, 0x59, 0x2f, 0xc7, 0x45, 0x7a, 0x69, 0xb9, 0xdd, 0xc5, 0x5f, + 0x1c, 0x56, 0x09, 0x98, 0xd6, 0xb1, 0x29, 0x52, 0x06, 0x29, 0x5f, 0x49, 0xe0, 0x06, 0x9d, 0x58, + 0xf2, 0x43, 0x89, 0x46, 0xde, 0x80, 0xdc, 0xdd, 0x9b, 0x25, 0x11, 0x46, 0x20, 0x45, 0xd2, 0xdd, + 0x9b, 0x25, 0xfe, 0x35, 0x73, 0x37, 0x29, 0xdd, 0x69, 0x8b, 0xe1, 0x93, 0x55, 0x25, 0xb7, 0xe8, + 0xb0, 0xf6, 0xfe, 0x8f, 0x04, 0x87, 0x9d, 0x3b, 0x3a, 0xc9, 0x28, 0x7f, 0xcb, 0x58, 0x64, 0xd9, + 0xfb, 0x77, 0x72, 0x30, 0x1a, 0xd6, 0x4f, 0x08, 0xa0, 0x52, 0x25, 0x4e, 0x32, 0xf8, 0x37, 0x39, + 0x0d, 0x05, 0xa9, 0x47, 0x89, 0x68, 0x82, 0x11, 0x5f, 0xe8, 0x50, 0x73, 0x20, 0x15, 0x26, 0xfe, + 0x99, 0x9b, 0xf2, 0x27, 0xb9, 0x06, 0xa1, 0x36, 0xd4, 0x4b, 0x6d, 0xca, 0xb3, 0x09, 0x33, 0x43, + 0x34, 0x32, 0x09, 0x59, 0x87, 0x67, 0x6e, 0x19, 0x35, 0xb3, 0x4e, 0x83, 0xbc, 0x07, 0x05, 0xbb, + 0xd1, 0xa0, 0x0d, 0xcb, 0x96, 0xce, 0x0f, 0xfd, 0x16, 0x4d, 0x81, 0x71, 0xe3, 0x9b, 0x00, 0x52, + 0x95, 0x02, 0x52, 0x82, 0xd1, 0xa6, 0xcd, 0x1d, 0xa9, 0x1a, 0x03, 0xec, 0x28, 0x11, 0x87, 0x02, + 0x23, 0xbb, 0xe7, 0xd3, 0x06, 0x79, 0x19, 0xf2, 0x6c, 0x36, 0xc5, 0x16, 0x22, 0xd5, 0x37, 0x36, + 0x99, 0x7c, 0xc0, 0x56, 0x9e, 0x33, 0x11, 0x81, 0xbc, 0x08, 0xb9, 0xee, 0xe2, 0x8e, 0xd8, 0x1c, + 0x8a, 0x51, 0x9e, 0xdf, 0x10, 0x8d, 0x15, 0x93, 0xeb, 0x50, 0xd8, 0xd7, 0x53, 0xc4, 0x9e, 0x88, + 0x4d, 0x63, 0x88, 0x1f, 0x22, 0x96, 0x0b, 0x30, 0xcc, 0x37, 0x02, 0xe3, 0x79, 0x80, 0xa8, 0xea, + 0x64, 0xd0, 0x87, 0xf1, 0x11, 0x8c, 0x86, 0x55, 0x92, 0x73, 0x00, 0x0f, 0xe8, 0x81, 0xb5, 0x67, + 0xb7, 0x1b, 0x4d, 0xae, 0xdf, 0x8d, 0x9b, 0xa3, 0x0f, 0xe8, 0xc1, 0x0a, 0x02, 0xc8, 0x29, 0x18, + 0xe9, 0xb0, 0x59, 0x15, 0x4b, 0x77, 0xdc, 0x1c, 0xee, 0x74, 0xb7, 0xd9, 0x0a, 0x9d, 0x83, 0x11, + 0xb4, 0xbc, 0x89, 0x0f, 0x6d, 0xc2, 0x94, 0x3f, 0x8d, 0xff, 0x3c, 0x8b, 0x4f, 0x19, 0x28, 0xed, + 0x24, 0x17, 0x60, 0xa2, 0xee, 0x51, 0xdc, 0x73, 0x6c, 0xa6, 0x49, 0x89, 0x7a, 0xc6, 0x23, 0x60, + 0xb5, 0x41, 0x2e, 0xc2, 0x94, 0x78, 0xbe, 0x9b, 0x35, 0xa8, 0xbe, 0x2d, 0xf2, 0x39, 0x8f, 0x9b, + 0x13, 0x1c, 0x7c, 0x87, 0x1e, 0x2c, 0x6d, 0x63, 0xc6, 0xa1, 0xa2, 0x9a, 0x30, 0x32, 0x08, 0x5f, + 0x5d, 0x34, 0xa7, 0x14, 0x38, 0xfa, 0x34, 0x9d, 0x84, 0x61, 0xdb, 0xde, 0xed, 0x3a, 0x3c, 0x33, + 0xc8, 0xb8, 0x29, 0x7e, 0x91, 0x57, 0x61, 0x3a, 0xca, 0x2b, 0x2a, 0xbb, 0x31, 0x84, 0xdd, 0x28, + 0x86, 0x05, 0x4b, 0x1c, 0x4e, 0x5e, 0x07, 0xa2, 0xd6, 0xe7, 0x6e, 0x7f, 0x4c, 0xeb, 0x7c, 0xa9, + 0x8d, 0x9b, 0xd3, 0x4a, 0xc9, 0x3a, 0x16, 0x90, 0x17, 0x60, 0xdc, 0xa3, 0x3e, 0x6a, 0x71, 0x38, + 0x6c, 0xf8, 0xd2, 0x8f, 0x39, 0x26, 0x61, 0x6c, 0xec, 0x2e, 0x41, 0x51, 0x19, 0x0e, 0xcc, 0xc9, + 0xc9, 0x93, 0x19, 0x9b, 0x93, 0x11, 0xdc, 0xec, 0x54, 0x1b, 0x46, 0x19, 0xa6, 0x13, 0x5f, 0xae, + 0xf2, 0x52, 0x2e, 0x97, 0x44, 0xfd, 0x5f, 0xca, 0x35, 0xda, 0x30, 0xae, 0x4a, 0xe2, 0x23, 0x72, + 0x6a, 0x9f, 0xc4, 0xc8, 0x72, 0x2e, 0xa6, 0x86, 0x1f, 0x1f, 0x2e, 0x64, 0x9d, 0x06, 0xc6, 0x93, + 0x5f, 0x82, 0x82, 0x54, 0x1a, 0xc4, 0x5e, 0x8d, 0x96, 0x53, 0xa1, 0xad, 0x1e, 0x98, 0x61, 0xa9, + 0xf1, 0x32, 0x8c, 0x08, 0x61, 0xdb, 0xdf, 0x5e, 0x6a, 0xfc, 0x48, 0x16, 0xa6, 0x4c, 0xca, 0x44, + 0x01, 0xe5, 0x89, 0xf4, 0x9f, 0xd9, 0xe3, 0x5b, 0x7a, 0x7e, 0x32, 0xad, 0x6f, 0x7d, 0x52, 0xd8, + 0xff, 0xbd, 0x0c, 0xcc, 0xa4, 0xe0, 0x7e, 0xaa, 0xe7, 0xd6, 0x6e, 0xc0, 0x68, 0xc5, 0xb1, 0x9b, + 0xa5, 0x46, 0x23, 0x0c, 0x33, 0x47, 0x55, 0xb3, 0xc1, 0x56, 0x9a, 0xcd, 0xa0, 0xea, 0xb6, 0x1b, + 0xa2, 0x92, 0x57, 0xc4, 0xa2, 0x88, 0x9e, 0xba, 0xc6, 0x45, 0xf1, 0xc9, 0xe1, 0x02, 0xf0, 0x36, + 0x45, 0x4f, 0x7a, 0x62, 0xce, 0x40, 0x0e, 0x8c, 0xdc, 0xc0, 0x9f, 0xd9, 0xa9, 0x4b, 0xcf, 0x19, + 0x18, 0xef, 0xde, 0x40, 0x59, 0xec, 0x7f, 0x3c, 0x0b, 0x27, 0xd3, 0x09, 0x3f, 0xed, 0xcb, 0x79, + 0xf8, 0x7e, 0x80, 0x92, 0xe7, 0x14, 0x5f, 0xce, 0xe3, 0x8f, 0x0d, 0x20, 0x7e, 0x84, 0x40, 0x76, + 0x60, 0x62, 0xd5, 0xf6, 0x83, 0x15, 0x6a, 0x7b, 0xc1, 0x36, 0xb5, 0x83, 0x01, 0x74, 0xcf, 0x17, + 0xe5, 0xb5, 0x24, 0x6e, 0x7f, 0x7b, 0x92, 0x32, 0xa6, 0x1d, 0xea, 0x6c, 0xc3, 0x85, 0x92, 0x1f, + 0x60, 0xa1, 0x7c, 0x0b, 0xa6, 0x6a, 0xb4, 0x65, 0x77, 0xf6, 0x5c, 0x4f, 0xc6, 0x11, 0x5e, 0x81, + 0x89, 0x10, 0x94, 0xba, 0x5a, 0xf4, 0x62, 0x0d, 0x5f, 0x19, 0x88, 0x48, 0x94, 0xe8, 0xc5, 0xc6, + 0xdf, 0xc8, 0xc2, 0xa9, 0x52, 0x5d, 0x78, 0x0b, 0x89, 0x02, 0xe9, 0xd4, 0xf8, 0x19, 0xd7, 0x4d, + 0xae, 0xc2, 0xe8, 0x5d, 0xfb, 0xd1, 0x2a, 0xb5, 0x7d, 0xea, 0x8b, 0x77, 0x8b, 0xb8, 0xa2, 0x66, + 0x3f, 0x8a, 0x9c, 0x68, 0xcc, 0x08, 0x47, 0x3d, 0xc9, 0xe6, 0x9f, 0xf0, 0x24, 0x6b, 0xc0, 0xf0, + 0x8a, 0xdb, 0x6c, 0x88, 0x6d, 0x4c, 0x5c, 0xaf, 0xed, 0x21, 0xc4, 0x14, 0x25, 0xec, 0x00, 0x38, + 0x19, 0xb6, 0x18, 0x9b, 0xf0, 0x99, 0x0f, 0xc9, 0x45, 0x18, 0xc1, 0x8a, 0xaa, 0x15, 0x75, 0xd3, + 0x68, 0x52, 0x7c, 0x7d, 0xa6, 0x61, 0xca, 0x42, 0x75, 0x24, 0x86, 0x9e, 0x6c, 0x24, 0x8c, 0x7f, + 0x17, 0x6f, 0xee, 0xd4, 0x5e, 0xb2, 0x9d, 0x48, 0x69, 0x48, 0x66, 0xc0, 0x86, 0x64, 0x9f, 0xda, + 0x94, 0xe4, 0x7a, 0x4e, 0xc9, 0x77, 0xb2, 0x30, 0x16, 0x36, 0xf6, 0x73, 0x96, 0x6c, 0x37, 0xec, + 0xd7, 0x40, 0xb1, 0xff, 0x35, 0x45, 0x56, 0x88, 0x10, 0xfb, 0xf7, 0x61, 0x58, 0x7c, 0x4c, 0x99, + 0x98, 0x73, 0x5f, 0x6c, 0x76, 0xcb, 0x93, 0x82, 0xf5, 0x30, 0x4e, 0xa8, 0x6f, 0x0a, 0x3a, 0x4c, + 0xae, 0xb0, 0x45, 0xb7, 0xc5, 0x45, 0xee, 0x33, 0xbb, 0x47, 0xa5, 0x27, 0x57, 0x88, 0x3a, 0x36, + 0xd0, 0xee, 0xf4, 0x8f, 0x0a, 0x50, 0x8c, 0x93, 0x1c, 0x9d, 0xce, 0x78, 0xa3, 0xbb, 0xcd, 0xb5, + 0x70, 0x9e, 0xce, 0xb8, 0xd3, 0xdd, 0x36, 0x19, 0x0c, 0xfd, 0x3c, 0x3c, 0xe7, 0x21, 0xf6, 0x7a, + 0x5c, 0xf8, 0x79, 0x78, 0xce, 0x43, 0xcd, 0xcf, 0xc3, 0x73, 0x1e, 0xe2, 0xd1, 0x77, 0xb5, 0x86, + 0xf1, 0xa0, 0xa8, 0x82, 0x8b, 0xa3, 0x6f, 0xd3, 0x8f, 0x3f, 0x31, 0x22, 0xd1, 0xd8, 0x56, 0x59, + 0xa6, 0xb6, 0x27, 0x52, 0xef, 0x0a, 0x71, 0x86, 0x5b, 0xe5, 0x36, 0x82, 0xf9, 0xeb, 0xbd, 0xa6, + 0x8a, 0x44, 0x9a, 0x40, 0x94, 0x9f, 0xf2, 0x03, 0x3e, 0xfa, 0x34, 0x28, 0x1d, 0x73, 0x66, 0x55, + 0xd6, 0x96, 0xfa, 0x35, 0xa7, 0xf0, 0x7d, 0x9a, 0x06, 0xc8, 0x0d, 0x91, 0x4f, 0x0c, 0x4d, 0x1e, + 0x85, 0x23, 0x99, 0xc9, 0x80, 0x69, 0xe0, 0xf9, 0xc6, 0x42, 0xc3, 0x47, 0xc4, 0x84, 0xbc, 0x0b, + 0x63, 0x6a, 0x94, 0x2f, 0x8f, 0x45, 0x3d, 0xcb, 0x53, 0x44, 0xf5, 0x78, 0x40, 0x4e, 0x25, 0x20, + 0xdb, 0x70, 0x6a, 0xc9, 0x6d, 0xfb, 0xdd, 0x96, 0x4c, 0x46, 0x15, 0xa5, 0xc0, 0x84, 0xf0, 0x01, + 0xf8, 0x17, 0xeb, 0x02, 0x45, 0x04, 0x95, 0x4a, 0xcf, 0x69, 0xfd, 0x00, 0xd2, 0x8b, 0x11, 0xd9, + 0x84, 0x31, 0x34, 0xe2, 0x09, 0xd7, 0xac, 0x31, 0x5d, 0x6c, 0x44, 0x25, 0x15, 0xf6, 0x61, 0xf0, + 0x6c, 0x2a, 0x76, 0xab, 0x29, 0x1d, 0x77, 0x55, 0x63, 0xa4, 0x82, 0x4c, 0x3e, 0x82, 0x49, 0x7e, + 0xdc, 0xdc, 0xa2, 0xdb, 0x7c, 0xed, 0x8c, 0x6b, 0x67, 0x67, 0xbd, 0x90, 0x5f, 0xf4, 0x0a, 0xd3, + 0xe9, 0x3e, 0xdd, 0xe6, 0x73, 0xaf, 0xb9, 0xcd, 0x6b, 0xf8, 0xe4, 0x1e, 0xcc, 0xac, 0xd8, 0x3e, + 0x07, 0x2a, 0xe1, 0x9a, 0x13, 0x68, 0x53, 0x44, 0x77, 0xc6, 0x3d, 0xdb, 0x97, 0xb6, 0xd8, 0xd4, + 0xf0, 0xcc, 0x34, 0x7a, 0xf2, 0x23, 0x19, 0x98, 0xd3, 0x4c, 0xb5, 0xc2, 0xa9, 0xa6, 0x45, 0xdb, + 0x01, 0xfa, 0xc7, 0x4f, 0x86, 0xef, 0x06, 0xf7, 0x42, 0xe3, 0x53, 0x12, 0xb3, 0x06, 0x7b, 0x51, + 0xb9, 0xea, 0x27, 0xd8, 0x8b, 0x87, 0xf8, 0x50, 0xf1, 0x9b, 0x9e, 0xd2, 0x3f, 0xd4, 0xd8, 0x77, + 0x2d, 0xd1, 0x8c, 0x1b, 0xf1, 0xf1, 0x16, 0xa6, 0x99, 0x4c, 0x68, 0x9a, 0x99, 0x85, 0x21, 0x1c, + 0x55, 0x99, 0x5d, 0x02, 0x7f, 0x18, 0x5f, 0x50, 0xe5, 0x90, 0x50, 0x0b, 0xfb, 0xca, 0x21, 0xe3, + 0xbf, 0x1d, 0x86, 0xa9, 0xd8, 0xb2, 0x10, 0xe7, 0xd4, 0x4c, 0xe2, 0x9c, 0x5a, 0x03, 0xe0, 0xc6, + 0xc9, 0x01, 0xad, 0x88, 0x32, 0x36, 0x67, 0x4c, 0xc4, 0xb6, 0x85, 0xdf, 0x94, 0xc2, 0x86, 0x31, + 0xe5, 0x5f, 0xec, 0x80, 0x56, 0xdd, 0x90, 0x29, 0xff, 0xe8, 0x15, 0xa6, 0x11, 0x1b, 0xb2, 0x00, + 0x43, 0x98, 0x12, 0x4e, 0x0d, 0x8d, 0x72, 0x18, 0xc0, 0xe4, 0x70, 0x72, 0x01, 0x86, 0x99, 0x12, + 0x55, 0xad, 0x08, 0x21, 0x88, 0x7b, 0x0b, 0xd3, 0xb2, 0x98, 0xc6, 0x22, 0x8a, 0xc8, 0x0d, 0x18, + 0xe7, 0x7f, 0x89, 0xac, 0x00, 0xc3, 0xba, 0xa7, 0x97, 0xe5, 0x34, 0x64, 0x62, 0x00, 0x0d, 0x8f, + 0x9d, 0x2e, 0x6a, 0xdd, 0x6d, 0xfe, 0xee, 0xbd, 0xc8, 0x21, 0x8a, 0xa7, 0x0b, 0x9f, 0x03, 0xf1, + 0x5d, 0xee, 0x10, 0x81, 0xe9, 0x32, 0xc2, 0x41, 0xb9, 0x80, 0x67, 0x4a, 0xd4, 0x65, 0xb8, 0x63, + 0xb2, 0x29, 0x4a, 0xc8, 0x65, 0x7e, 0x19, 0x80, 0x6a, 0x21, 0x7f, 0x52, 0x09, 0x2d, 0xed, 0x68, + 0x98, 0x40, 0xdd, 0x30, 0x2c, 0x66, 0x95, 0xb3, 0xbf, 0x97, 0x5b, 0xb6, 0xd3, 0x14, 0x62, 0x05, + 0x2b, 0x47, 0x5c, 0xca, 0xa0, 0x66, 0x84, 0x40, 0xde, 0x86, 0x49, 0xf6, 0x63, 0xc9, 0x6d, 0xb5, + 0xdc, 0x36, 0xb2, 0x1f, 0x8b, 0x12, 0xcc, 0x20, 0x49, 0x1d, 0x8b, 0x78, 0x2d, 0x31, 0x5c, 0xb6, + 0x9f, 0xe0, 0x45, 0x63, 0x97, 0x5f, 0x53, 0x8c, 0x47, 0xfb, 0x09, 0x92, 0xfa, 0x1c, 0x6e, 0xaa, + 0x48, 0xe4, 0x4d, 0x98, 0x60, 0x3f, 0x6f, 0x39, 0x0f, 0x29, 0xaf, 0x70, 0x22, 0xba, 0xfa, 0x46, + 0xaa, 0x5d, 0x56, 0xc2, 0xeb, 0xd3, 0x31, 0xc9, 0x57, 0xe1, 0x04, 0x72, 0xaa, 0xbb, 0x1d, 0xda, + 0x28, 0xed, 0xec, 0x38, 0x4d, 0x87, 0xbb, 0xde, 0xf0, 0xf8, 0x77, 0xb4, 0x1a, 0xf3, 0x8a, 0x11, + 0xc3, 0xb2, 0x23, 0x14, 0x33, 0x9d, 0x92, 0x6c, 0x41, 0x71, 0xa9, 0xeb, 0x07, 0x6e, 0xab, 0x14, + 0x04, 0x9e, 0xb3, 0xdd, 0x0d, 0xa8, 0x3f, 0x37, 0xa5, 0x45, 0x89, 0xb3, 0x8f, 0x23, 0x2c, 0xe4, + 0xf6, 0xa0, 0x3a, 0x52, 0x58, 0x76, 0x48, 0x62, 0x26, 0x98, 0x18, 0xff, 0x4d, 0x06, 0x26, 0x34, + 0x52, 0xf2, 0x06, 0x8c, 0xdf, 0xf4, 0x1c, 0xda, 0x6e, 0x34, 0x0f, 0x94, 0x83, 0x2a, 0x9e, 0x62, + 0x76, 0x04, 0x9c, 0xf7, 0x5a, 0x43, 0x0b, 0xed, 0x3c, 0xd9, 0x54, 0xbf, 0xb8, 0xab, 0x3c, 0x42, + 0x4f, 0x2c, 0xd0, 0x5c, 0x94, 0xb6, 0x02, 0x17, 0xa8, 0x58, 0x9d, 0x0a, 0x0a, 0x79, 0x07, 0x86, + 0xf9, 0x95, 0xa4, 0x70, 0xd2, 0x3a, 0x9d, 0xd6, 0x4d, 0x1e, 0x0d, 0x8a, 0x0b, 0x11, 0x1d, 0x42, + 0x7c, 0x53, 0x10, 0x19, 0x3f, 0x93, 0x01, 0x92, 0x44, 0x3d, 0xc2, 0xee, 0x75, 0xa4, 0xa3, 0xc9, + 0xfb, 0xe1, 0xd7, 0x98, 0xd3, 0xac, 0xbc, 0xac, 0x26, 0x5e, 0xc0, 0x07, 0x5e, 0x7c, 0x75, 0xaa, + 0x21, 0x8e, 0x17, 0x1b, 0x7f, 0x39, 0x0b, 0x10, 0x61, 0x93, 0x2f, 0xf3, 0x97, 0x37, 0xbe, 0xda, + 0xb5, 0x9b, 0xce, 0x8e, 0xa3, 0xa7, 0xa2, 0x43, 0x26, 0xdf, 0x92, 0x25, 0xa6, 0x8e, 0x48, 0xde, + 0x83, 0xa9, 0xda, 0x86, 0x4e, 0xab, 0xbc, 0x32, 0xe0, 0x77, 0xac, 0x18, 0x79, 0x1c, 0x1b, 0x9d, + 0x31, 0xd5, 0xd9, 0xe0, 0xce, 0x98, 0x7c, 0x22, 0x44, 0x09, 0x13, 0x2c, 0xb5, 0x0d, 0xe1, 0xff, + 0xdb, 0xa8, 0x56, 0x84, 0x94, 0xc2, 0xd6, 0xf9, 0x1d, 0xab, 0x23, 0x1c, 0x83, 0x99, 0x9c, 0xd0, + 0xf0, 0xa2, 0x81, 0x1c, 0xea, 0x11, 0xf1, 0xf9, 0xb3, 0x68, 0xf6, 0x6b, 0xb9, 0x01, 0x15, 0xd6, + 0x8e, 0x67, 0xf6, 0xdc, 0x13, 0xdd, 0x67, 0x0f, 0x69, 0x81, 0x6c, 0x5a, 0xef, 0x84, 0x37, 0xc5, + 0xf5, 0xe8, 0x90, 0xc2, 0x6f, 0xb6, 0x53, 0xfc, 0x2f, 0xfe, 0x4e, 0x06, 0x4e, 0xa4, 0xd2, 0x92, + 0x2b, 0x00, 0x91, 0x4d, 0x49, 0x8c, 0x12, 0x4a, 0xcc, 0x28, 0x59, 0x83, 0xa9, 0x60, 0x90, 0xaf, + 0xc7, 0xad, 0x41, 0x47, 0x6f, 0x84, 0xf3, 0x32, 0x19, 0x8f, 0x6e, 0x0d, 0x4a, 0xb1, 0x01, 0x19, + 0x7f, 0x2f, 0x07, 0xd3, 0x4a, 0x2e, 0x08, 0xde, 0xd6, 0x23, 0x9c, 0x63, 0x1f, 0xc0, 0x38, 0xeb, + 0x8d, 0x53, 0x17, 0xd1, 0x34, 0xdc, 0xf7, 0xe2, 0x95, 0x44, 0x28, 0x92, 0xe0, 0x76, 0x45, 0x45, + 0xe6, 0x29, 0xb2, 0x50, 0x74, 0xa2, 0xad, 0xbd, 0x9e, 0x8c, 0xaa, 0xd1, 0x98, 0x13, 0x1f, 0x26, + 0x2a, 0x07, 0x6d, 0xbb, 0x15, 0xd6, 0xc6, 0x7d, 0x30, 0x5e, 0xed, 0x59, 0x9b, 0x86, 0xcd, 0xab, + 0x8b, 0x9c, 0xf6, 0x79, 0x59, 0x4a, 0xbc, 0xa8, 0x46, 0x35, 0xff, 0x1e, 0x4c, 0x27, 0x1a, 0x7d, + 0xac, 0x6c, 0x5d, 0x5b, 0x40, 0x92, 0xed, 0x48, 0xe1, 0xf0, 0xaa, 0x9e, 0x0b, 0xee, 0x44, 0x78, + 0xdd, 0x8a, 0xef, 0x01, 0x73, 0x8f, 0x8e, 0x45, 0x35, 0x97, 0xd7, 0xcf, 0x66, 0xd5, 0x70, 0xb0, + 0x67, 0xfd, 0xab, 0x7b, 0x5f, 0x3b, 0x0d, 0x3f, 0xdf, 0x6b, 0x4e, 0x07, 0xb2, 0x3a, 0x7c, 0x37, + 0x07, 0xa7, 0x7a, 0x50, 0x92, 0x83, 0xf8, 0x22, 0xe2, 0x56, 0x88, 0x6b, 0xfd, 0x2b, 0x7c, 0x1a, + 0x4b, 0x89, 0x7c, 0x99, 0x07, 0x84, 0xd7, 0xf1, 0x1d, 0x5b, 0x71, 0xfe, 0xe6, 0x4f, 0xa0, 0x87, + 0xd0, 0x78, 0x24, 0x38, 0x87, 0x92, 0xf7, 0x60, 0x08, 0x63, 0x01, 0x63, 0xb9, 0xa8, 0x18, 0x06, + 0xc2, 0x95, 0xc4, 0x5d, 0xec, 0xa7, 0x96, 0xb8, 0x8b, 0x01, 0xc8, 0x97, 0x20, 0x57, 0xda, 0xaa, + 0x89, 0x79, 0x99, 0x54, 0xc9, 0xb7, 0x6a, 0x51, 0xbe, 0x70, 0x5b, 0x4b, 0xec, 0xcd, 0x28, 0x18, + 0xe1, 0xad, 0xa5, 0x0d, 0x31, 0x2b, 0x2a, 0xe1, 0xad, 0xa5, 0x8d, 0x88, 0x70, 0xb7, 0xae, 0xe5, + 0xf6, 0xb8, 0xb5, 0xb4, 0xf1, 0xd9, 0x2d, 0xfb, 0xbf, 0x96, 0xe5, 0x51, 0xec, 0xbc, 0x63, 0xef, + 0xc1, 0xb8, 0x96, 0xab, 0x33, 0x13, 0xe9, 0x63, 0x61, 0x4a, 0xd4, 0x98, 0xd3, 0x8a, 0x46, 0x20, + 0x33, 0xef, 0xb3, 0xdf, 0xa8, 0xf1, 0xaa, 0xee, 0x21, 0x21, 0x07, 0xd4, 0x89, 0xe3, 0x99, 0xf7, + 0x43, 0x12, 0x72, 0x1d, 0x0a, 0x9b, 0xb4, 0x6d, 0xb7, 0x83, 0xd0, 0x20, 0x8a, 0x8e, 0xa7, 0x01, + 0xc2, 0x74, 0xad, 0x21, 0x44, 0x44, 0x27, 0xc9, 0xee, 0xb6, 0x5f, 0xf7, 0x1c, 0xcc, 0x76, 0x11, + 0xee, 0xc5, 0xdc, 0x49, 0x52, 0x29, 0xd1, 0x19, 0xc4, 0x88, 0x8c, 0x9f, 0xcd, 0xc0, 0x88, 0x98, + 0x48, 0xfe, 0x62, 0xca, 0x6e, 0xb4, 0x97, 0x88, 0x17, 0x53, 0x76, 0x9d, 0xf8, 0x8b, 0x29, 0xbb, + 0x3c, 0xa5, 0xc4, 0xa8, 0x08, 0xc8, 0x0c, 0xaf, 0x06, 0xf9, 0xe3, 0xdd, 0x1c, 0xa8, 0x57, 0x1b, + 0xa1, 0x0e, 0x1a, 0x7d, 0x62, 0xfc, 0x2d, 0xd1, 0xb2, 0x5b, 0x4b, 0x1b, 0x64, 0x11, 0x0a, 0xab, + 0x6e, 0xdd, 0x56, 0xf6, 0x39, 0x14, 0x3b, 0x4d, 0x01, 0x53, 0x07, 0x48, 0xe2, 0xb1, 0xf6, 0x6d, + 0x78, 0xae, 0x38, 0xcb, 0x28, 0xed, 0xeb, 0x70, 0x60, 0xac, 0x7d, 0x21, 0xea, 0xc0, 0xed, 0xa3, + 0x29, 0x42, 0xe2, 0xfe, 0x75, 0x4c, 0x49, 0x7e, 0x5b, 0x8d, 0xea, 0x11, 0x45, 0x52, 0x52, 0xcc, + 0xf7, 0x92, 0x14, 0xf7, 0xaf, 0x9b, 0x29, 0x54, 0x78, 0xaf, 0x16, 0x81, 0x6b, 0xd4, 0x7b, 0xf8, + 0x0c, 0x4b, 0xe9, 0xf4, 0x7b, 0xb5, 0x78, 0xf7, 0x06, 0x12, 0xd2, 0xff, 0x55, 0x16, 0x4e, 0xa6, + 0x13, 0xaa, 0x7d, 0xc9, 0xf4, 0xe9, 0xcb, 0x25, 0x28, 0xac, 0xb8, 0x7e, 0xa0, 0xf8, 0xa9, 0xa1, + 0xf9, 0x7f, 0x4f, 0xc0, 0xcc, 0xb0, 0x94, 0x9d, 0xb9, 0xd9, 0xdf, 0xe1, 0xe7, 0x89, 0xfc, 0x30, + 0x76, 0x9b, 0x9d, 0xb9, 0x79, 0x11, 0xb9, 0x05, 0x05, 0x53, 0x44, 0x95, 0xc4, 0x86, 0x46, 0x82, + 0x43, 0x6d, 0x8a, 0x78, 0x02, 0xa2, 0xa5, 0x4c, 0x15, 0x30, 0x52, 0x82, 0x11, 0x31, 0xfb, 0xb1, + 0xab, 0xe3, 0x94, 0x25, 0xa3, 0x67, 0x31, 0x96, 0x74, 0x4c, 0xa2, 0xe0, 0x25, 0x60, 0xb5, 0x22, + 0x03, 0x44, 0x50, 0xa2, 0xf0, 0x4b, 0x42, 0xdd, 0x25, 0x30, 0x44, 0x34, 0x7e, 0x24, 0x0b, 0x20, + 0xad, 0x36, 0xcf, 0xec, 0x0a, 0xfb, 0x92, 0xb6, 0xc2, 0x14, 0x0f, 0x99, 0xc1, 0x5f, 0xf8, 0x5b, + 0x47, 0x4f, 0x95, 0xc1, 0xdf, 0xf7, 0x5b, 0x80, 0xa1, 0xcd, 0xc8, 0xa0, 0x25, 0xc2, 0x15, 0xd0, + 0x1c, 0xcd, 0xe1, 0xc6, 0x36, 0xcc, 0xde, 0xa2, 0x41, 0x64, 0xde, 0x92, 0x57, 0x8f, 0xfd, 0xd9, + 0xbe, 0x06, 0xa3, 0x02, 0x3f, 0x94, 0x5f, 0xdc, 0x16, 0x23, 0xd2, 0x21, 0xa0, 0x2d, 0x46, 0x22, + 0x30, 0x69, 0x54, 0xa1, 0x4d, 0x1a, 0xd0, 0xcf, 0xb6, 0x9a, 0x1a, 0x10, 0xde, 0x15, 0xec, 0xd9, + 0x60, 0x35, 0x1c, 0x39, 0x3e, 0xf7, 0xe1, 0x44, 0xd8, 0xf6, 0xa7, 0xc9, 0xf7, 0x2a, 0x3b, 0x52, + 0x8a, 0x04, 0xc0, 0x11, 0xc7, 0x3e, 0xbe, 0x27, 0x8f, 0x60, 0x5e, 0x12, 0x6c, 0x39, 0xa1, 0xab, + 0xdf, 0x40, 0xb4, 0xe4, 0x6d, 0x18, 0x53, 0x68, 0x44, 0x02, 0x5b, 0x34, 0x53, 0xef, 0x3b, 0xc1, + 0x9e, 0xe5, 0x73, 0xb8, 0x6a, 0xa6, 0x56, 0xd0, 0x8d, 0x0f, 0xe1, 0x4c, 0x18, 0x52, 0x92, 0x52, + 0x75, 0x8c, 0x79, 0xe6, 0x78, 0xcc, 0xd7, 0xa2, 0x6e, 0x55, 0xdb, 0x61, 0x18, 0xa8, 0xe4, 0x4d, + 0xd4, 0x6e, 0x89, 0xce, 0x9c, 0x4d, 0x04, 0x96, 0x2a, 0xf1, 0xa3, 0xc6, 0x5b, 0x4a, 0x63, 0x53, + 0x18, 0x6a, 0xc4, 0x99, 0x38, 0xf1, 0x8f, 0x64, 0x61, 0x6a, 0xbd, 0x5a, 0x59, 0x0a, 0xbd, 0x8f, + 0x3e, 0x67, 0xef, 0x0f, 0x6a, 0x7d, 0xeb, 0x2d, 0x6f, 0x8c, 0x7b, 0x30, 0x13, 0x1b, 0x06, 0x54, + 0x1d, 0xde, 0xe5, 0x31, 0x0f, 0x21, 0x58, 0xaa, 0x0d, 0x27, 0xd3, 0xd8, 0xdf, 0xbf, 0x6e, 0xc6, + 0xb0, 0x8d, 0x7f, 0x3c, 0x1a, 0xe3, 0x2b, 0x44, 0xd8, 0x6b, 0x30, 0x5a, 0xf5, 0xfd, 0x2e, 0xf5, + 0xee, 0x99, 0xab, 0xaa, 0xa9, 0xc0, 0x41, 0xa0, 0xd5, 0xf5, 0x9a, 0x66, 0x84, 0x40, 0x2e, 0x43, + 0x41, 0xe4, 0x74, 0x95, 0x32, 0x01, 0xad, 0xb6, 0x61, 0x4a, 0x58, 0x33, 0x2c, 0x26, 0x6f, 0xc0, + 0x38, 0xff, 0x9b, 0xaf, 0x36, 0x31, 0xe0, 0x68, 0x1c, 0x14, 0xe8, 0x7c, 0x75, 0x9a, 0x1a, 0x1a, + 0x79, 0x05, 0x72, 0xa5, 0x25, 0x53, 0x98, 0x83, 0x84, 0xde, 0x88, 0xaf, 0x0a, 0x77, 0xa9, 0x7e, + 0x88, 0x58, 0x32, 0x99, 0xf6, 0x27, 0x43, 0xce, 0x85, 0x25, 0x9b, 0x3f, 0x7e, 0x2c, 0x60, 0xb1, + 0xcd, 0x0c, 0x61, 0xe4, 0x2a, 0x8c, 0x54, 0x1c, 0xbf, 0xd3, 0xb4, 0x0f, 0x84, 0x1d, 0x9b, 0x3f, + 0xae, 0xc3, 0x41, 0x5a, 0x24, 0x39, 0x07, 0x91, 0xcb, 0xf2, 0xd1, 0x91, 0x42, 0x14, 0x3a, 0xd1, + 0xe3, 0x65, 0x91, 0xd7, 0x60, 0x58, 0x64, 0x3e, 0x1d, 0x55, 0x72, 0x9a, 0xc7, 0x33, 0x9e, 0x0a, + 0x9c, 0x64, 0x70, 0x23, 0x3c, 0xcd, 0xe0, 0xc6, 0x6d, 0x38, 0x75, 0x0b, 0xad, 0x37, 0x7a, 0x8e, + 0x94, 0x7b, 0x66, 0x55, 0xd8, 0xc3, 0xf1, 0x1a, 0x88, 0x1b, 0x78, 0xe2, 0x69, 0x56, 0xac, 0xae, + 0xa7, 0xbe, 0x15, 0xd7, 0x8b, 0x11, 0xf9, 0x1a, 0xcc, 0xa6, 0x15, 0x09, 0xab, 0x39, 0x66, 0x03, + 0x49, 0xaf, 0x40, 0xcd, 0x06, 0x92, 0xc6, 0x81, 0xac, 0x42, 0x91, 0xc3, 0x4b, 0x8d, 0x96, 0xd3, + 0xe6, 0x96, 0x7f, 0x6e, 0x55, 0xc7, 0x58, 0x06, 0xc1, 0xd5, 0x66, 0x85, 0xfc, 0x06, 0x40, 0x8b, + 0x7e, 0x89, 0x51, 0x92, 0x9f, 0xcc, 0xb0, 0xd3, 0x1c, 0xcf, 0x13, 0x7a, 0xcf, 0x5c, 0xf5, 0x45, + 0x26, 0xa9, 0x93, 0x51, 0x60, 0x4b, 0x2d, 0xf0, 0x9c, 0xf6, 0xae, 0x88, 0x6c, 0xd9, 0x14, 0x91, + 0x2d, 0x6f, 0x7f, 0xaa, 0xc8, 0x16, 0xce, 0xca, 0x7f, 0x7c, 0xb8, 0x30, 0xee, 0x89, 0x3a, 0xf1, + 0x2b, 0xd2, 0x5a, 0x80, 0xef, 0x9c, 0x37, 0x9b, 0xee, 0xfe, 0xbd, 0xf6, 0x43, 0xea, 0x39, 0x3b, + 0x0e, 0x6d, 0xf0, 0x4e, 0x4e, 0xa1, 0x04, 0xe7, 0xef, 0x9c, 0xb3, 0x72, 0xab, 0x1b, 0x22, 0x24, + 0x3a, 0x9a, 0xca, 0x81, 0x1d, 0x3c, 0x65, 0xf4, 0x04, 0x8f, 0xd4, 0x2c, 0x46, 0x07, 0x4f, 0x19, + 0x6a, 0x61, 0xe1, 0x32, 0x52, 0x17, 0x8f, 0x46, 0x42, 0xae, 0xc2, 0xf0, 0x5d, 0xfb, 0x51, 0x69, + 0x97, 0x8a, 0xc7, 0xa4, 0x26, 0xa4, 0xf8, 0x43, 0x60, 0xb9, 0xf0, 0xfb, 0xdc, 0x3b, 0xff, 0x39, + 0x53, 0xa0, 0x91, 0x1f, 0xc8, 0xc0, 0x49, 0xfe, 0x19, 0xcb, 0x5e, 0xd6, 0x68, 0x10, 0xb0, 0x71, + 0x10, 0x29, 0xa5, 0xe4, 0x53, 0x0c, 0xb5, 0xda, 0x7a, 0x3a, 0x1e, 0x7f, 0x95, 0x5b, 0x48, 0x86, + 0x70, 0xe0, 0x7c, 0x51, 0xaa, 0xe5, 0x8d, 0x4c, 0xa5, 0x17, 0x9e, 0xe7, 0x5f, 0x92, 0x2d, 0x27, + 0xaf, 0xab, 0x01, 0x85, 0x39, 0xd4, 0x73, 0x47, 0x5a, 0xf6, 0x23, 0xcb, 0xde, 0xa5, 0xda, 0x7d, + 0x76, 0x18, 0x69, 0x78, 0xba, 0x67, 0xdb, 0xc8, 0x0d, 0x38, 0x25, 0xdf, 0x67, 0xdf, 0x0b, 0x82, + 0x8e, 0x6f, 0xc9, 0xb3, 0x80, 0x88, 0x40, 0x34, 0x4f, 0x88, 0xe2, 0x15, 0x56, 0x2a, 0x8f, 0x07, + 0xbe, 0xf1, 0x47, 0xa3, 0x7c, 0x4f, 0x2b, 0x75, 0x83, 0x3d, 0xb9, 0x0b, 0x2e, 0xa6, 0xc5, 0xd0, + 0x70, 0xe7, 0x3e, 0x25, 0x86, 0x46, 0x8f, 0x9c, 0x91, 0x97, 0x11, 0xd9, 0xd4, 0xcb, 0x88, 0xd7, + 0x60, 0x74, 0x69, 0x8f, 0xd6, 0x1f, 0x84, 0x71, 0x0c, 0x05, 0x61, 0xed, 0x65, 0x40, 0x9e, 0x52, + 0x34, 0x42, 0x20, 0x57, 0x01, 0x30, 0xa8, 0x8e, 0xab, 0x48, 0x4a, 0x5a, 0x70, 0x8c, 0xc1, 0x13, + 0xfe, 0x12, 0x0a, 0x0a, 0xb2, 0xaf, 0x99, 0x37, 0x55, 0x07, 0x0b, 0xce, 0xde, 0xf7, 0x76, 0x04, + 0x7a, 0x84, 0xc0, 0xba, 0xa7, 0x2c, 0x74, 0x21, 0x96, 0x8b, 0x89, 0xaf, 0x41, 0x45, 0x42, 0xdf, + 0x45, 0xe9, 0xb4, 0x8d, 0x52, 0x79, 0x5c, 0xf8, 0x2e, 0x86, 0x0e, 0xde, 0x66, 0x84, 0x40, 0xbe, + 0x04, 0x23, 0x4b, 0xd4, 0x0b, 0x36, 0x37, 0x57, 0xd1, 0x07, 0x82, 0xe7, 0xce, 0x2e, 0x60, 0x9e, + 0xe3, 0x20, 0x68, 0x7e, 0x72, 0xb8, 0x30, 0x11, 0x38, 0x2d, 0x7a, 0x25, 0x9c, 0x60, 0x89, 0x4d, + 0xca, 0x50, 0xe4, 0xb7, 0xb4, 0x91, 0x2a, 0x8c, 0x82, 0xba, 0xc0, 0xb7, 0x0d, 0x71, 0xa5, 0xbb, + 0x4f, 0xb7, 0xc3, 0x2c, 0xcf, 0x09, 0x7c, 0xb2, 0x2c, 0x93, 0xa3, 0xab, 0x9d, 0x84, 0xc8, 0x36, + 0x13, 0x5f, 0xc0, 0xac, 0xaf, 0x49, 0x0a, 0x52, 0x82, 0x89, 0x25, 0xb7, 0xd5, 0xb1, 0x03, 0x07, + 0x5f, 0x1a, 0x3a, 0x10, 0x32, 0x19, 0xed, 0x4b, 0x75, 0xb5, 0x40, 0x13, 0xf0, 0x6a, 0x01, 0xb9, + 0x09, 0x93, 0xa6, 0xdb, 0x65, 0x93, 0x24, 0x0f, 0x85, 0x5c, 0xec, 0xa2, 0xa7, 0x82, 0xc7, 0x4a, + 0xd8, 0x2e, 0x21, 0x4e, 0x80, 0x5a, 0x8e, 0x3a, 0x8d, 0x8a, 0xac, 0xa5, 0x58, 0xe7, 0x55, 0x59, + 0xab, 0xe6, 0x7a, 0x4e, 0x30, 0x4b, 0x31, 0xec, 0x5f, 0x87, 0xb1, 0x5a, 0x6d, 0x7d, 0x93, 0xfa, + 0xc1, 0xcd, 0xa6, 0xbb, 0x8f, 0xa2, 0xb6, 0x20, 0xde, 0xc0, 0xf0, 0x5d, 0x2b, 0xa0, 0x7e, 0x60, + 0xed, 0x34, 0xdd, 0x7d, 0x53, 0xc5, 0x22, 0xdf, 0x60, 0xe3, 0xa1, 0x28, 0x26, 0x22, 0x1b, 0x5f, + 0x3f, 0xdd, 0x09, 0x05, 0x5a, 0xf4, 0xc9, 0x30, 0x0d, 0x4a, 0x1f, 0x2c, 0x05, 0x1d, 0x83, 0x72, + 0xd8, 0x71, 0xb6, 0xd4, 0x68, 0x78, 0xd4, 0xf7, 0x85, 0x4c, 0xe4, 0x41, 0x39, 0x78, 0xf6, 0xb5, + 0x79, 0x81, 0x16, 0x94, 0xa3, 0x10, 0x90, 0xef, 0x64, 0xe0, 0x84, 0xea, 0xd7, 0x8f, 0x1f, 0x0b, + 0x7a, 0x5d, 0x70, 0x09, 0xf9, 0xfa, 0x15, 0xb9, 0x27, 0x5c, 0x51, 0xd0, 0xae, 0x3c, 0xbc, 0x76, + 0xa5, 0x14, 0xfd, 0xac, 0x49, 0x22, 0x91, 0xd0, 0x2a, 0x8d, 0x9f, 0x2a, 0xdf, 0xed, 0x14, 0x52, + 0xb2, 0xc4, 0xd4, 0x06, 0xb6, 0x9e, 0xd0, 0x8b, 0xa7, 0xba, 0x81, 0x02, 0x56, 0x98, 0xf7, 0xc4, + 0xea, 0xe3, 0xfe, 0x3e, 0x4e, 0x47, 0xd7, 0x0e, 0x14, 0x1a, 0x52, 0x85, 0x29, 0x0e, 0x60, 0x22, + 0x81, 0x3f, 0x90, 0x30, 0x13, 0x25, 0x69, 0x16, 0x6c, 0xf0, 0xe2, 0x19, 0x1f, 0x49, 0x50, 0x93, + 0xc7, 0xc5, 0xe8, 0x50, 0x6f, 0xaf, 0x95, 0xee, 0xae, 0x46, 0xca, 0xe7, 0xe7, 0xcb, 0x2f, 0x5f, + 0xeb, 0x5b, 0x1f, 0xbf, 0xfc, 0x7b, 0x3c, 0x52, 0x51, 0x19, 0x06, 0xa9, 0xb7, 0x6b, 0xe0, 0xb8, + 0xde, 0x1e, 0xa3, 0x31, 0x63, 0xd8, 0xc6, 0x27, 0x85, 0x18, 0x5f, 0xe1, 0x8b, 0x67, 0xc0, 0x30, + 0x57, 0xcb, 0xd5, 0xd7, 0xb6, 0xb9, 0xd2, 0x6e, 0x8a, 0x12, 0x72, 0x1a, 0x72, 0xb5, 0xda, 0xba, + 0x18, 0x64, 0xf4, 0xc8, 0xf3, 0x7d, 0xd7, 0x64, 0x30, 0x36, 0x43, 0xe8, 0x66, 0xa7, 0xe4, 0xe1, + 0x65, 0x12, 0xd4, 0x44, 0x28, 0x1b, 0x6f, 0xa9, 0x24, 0xe7, 0xa3, 0xf1, 0x16, 0x4a, 0x72, 0xa4, + 0x1a, 0x2f, 0xc1, 0x5c, 0xc9, 0xf7, 0xa9, 0xc7, 0x16, 0xa8, 0xf0, 0xde, 0xf2, 0x84, 0x22, 0x27, + 0x36, 0x0a, 0xac, 0xd4, 0xae, 0xfb, 0x66, 0x4f, 0x44, 0x72, 0x09, 0x0a, 0xa5, 0x6e, 0xc3, 0xa1, + 0xed, 0xba, 0x96, 0x64, 0xc7, 0x16, 0x30, 0x33, 0x2c, 0x25, 0x5f, 0x85, 0x13, 0xb1, 0x44, 0x53, + 0x62, 0x04, 0x46, 0xa2, 0xaf, 0x59, 0x2a, 0x9a, 0xd1, 0x8d, 0x33, 0x1f, 0x92, 0x74, 0x4a, 0x52, + 0x82, 0xe2, 0x32, 0xc6, 0xa1, 0x54, 0x28, 0x37, 0x7e, 0xbb, 0x1e, 0x8f, 0xad, 0xe1, 0xc7, 0x02, + 0x1e, 0xa3, 0x62, 0x35, 0xc2, 0x42, 0x33, 0x81, 0x4e, 0xee, 0xc0, 0x4c, 0x1c, 0xc6, 0xf6, 0x04, + 0x7e, 0x02, 0xc0, 0x44, 0x90, 0x09, 0x2e, 0xb8, 0x2b, 0xa4, 0x51, 0x91, 0x6d, 0x98, 0x8e, 0x3c, + 0x2e, 0xf4, 0x73, 0x81, 0x74, 0xe4, 0x0c, 0xcb, 0xe5, 0xd9, 0xe0, 0x8c, 0x58, 0x8c, 0x33, 0x91, + 0xf7, 0x46, 0x78, 0x3e, 0x30, 0x93, 0xec, 0x48, 0x03, 0x26, 0x6b, 0xce, 0x6e, 0xdb, 0x69, 0xef, + 0xde, 0xa1, 0x07, 0x1b, 0xb6, 0xe3, 0x09, 0x97, 0x3a, 0xe9, 0x30, 0x5b, 0xf2, 0x0f, 0x5a, 0x2d, + 0x1a, 0x78, 0xb8, 0xdb, 0xb2, 0x72, 0x0c, 0x0b, 0x65, 0xfa, 0xde, 0xbc, 0xcf, 0xe9, 0x30, 0xe4, + 0xaa, 0x63, 0x3b, 0xda, 0xb6, 0xa2, 0xf3, 0xd4, 0xce, 0x66, 0xe3, 0x03, 0x9e, 0xcd, 0x9a, 0x30, + 0xbd, 0xdc, 0xae, 0x7b, 0x07, 0x78, 0x07, 0x21, 0x1b, 0x37, 0x71, 0x44, 0xe3, 0x5e, 0x14, 0x8d, + 0x3b, 0x6b, 0xcb, 0x15, 0x96, 0xd6, 0xbc, 0x24, 0x63, 0x52, 0x83, 0x69, 0x54, 0xa0, 0xab, 0x95, + 0x8d, 0x6a, 0xdb, 0x09, 0x1c, 0x7c, 0x13, 0x9a, 0x6f, 0x57, 0x2f, 0x09, 0x9e, 0xe7, 0xb8, 0x0e, + 0xee, 0x34, 0x3a, 0x96, 0x23, 0x51, 0x54, 0xa6, 0x09, 0xfa, 0x7e, 0x8a, 0xf0, 0xd4, 0xbf, 0x18, + 0x45, 0x18, 0x5f, 0x4d, 0x8a, 0x85, 0x4b, 0x17, 0x23, 0xd9, 0xee, 0x63, 0x11, 0xdb, 0x22, 0xdc, + 0x2e, 0xaa, 0x27, 0xda, 0xab, 0x49, 0x3a, 0x9d, 0xf1, 0x9d, 0x51, 0x2e, 0xdb, 0x55, 0xfd, 0xb5, + 0x97, 0x2b, 0x5d, 0x4c, 0xaf, 0xcd, 0x1e, 0x47, 0xaf, 0xcd, 0x1d, 0xad, 0xd7, 0xe6, 0x8f, 0xd2, + 0x6b, 0x63, 0x8a, 0xe7, 0xd0, 0xb1, 0x15, 0xcf, 0xe1, 0x63, 0x28, 0x9e, 0x23, 0xc7, 0x52, 0x3c, + 0x35, 0x0d, 0xba, 0x70, 0x94, 0x06, 0xfd, 0x17, 0x6a, 0xea, 0xb3, 0xaa, 0xa6, 0xa6, 0xa9, 0x0a, + 0xc7, 0x52, 0x53, 0x7b, 0x6b, 0x99, 0xc5, 0x7f, 0xd9, 0x5a, 0xe6, 0xf4, 0xd3, 0xd1, 0x32, 0xc9, + 0xa7, 0xd4, 0x32, 0xbf, 0x0f, 0x8a, 0xf1, 0x8d, 0xef, 0xe8, 0xfc, 0x7a, 0x4f, 0x2d, 0x17, 0x14, + 0xdb, 0x96, 0xe3, 0x1b, 0x0f, 0x3b, 0x48, 0x6f, 0x78, 0xce, 0x43, 0x3b, 0xa0, 0x77, 0xa4, 0xeb, + 0x81, 0xc8, 0x0d, 0xc9, 0xa1, 0x28, 0x3e, 0x14, 0x94, 0x50, 0xe7, 0xca, 0xa6, 0xe9, 0x5c, 0xc6, + 0x8f, 0x66, 0x61, 0x9a, 0xe7, 0x6d, 0x79, 0xf6, 0x2d, 0xe0, 0xef, 0x6a, 0x9a, 0xb4, 0x74, 0x74, + 0x8b, 0xf5, 0xae, 0x8f, 0x0d, 0xfc, 0x23, 0x38, 0x91, 0x18, 0x0a, 0xd4, 0xa6, 0x2b, 0x32, 0x63, + 0x4e, 0x42, 0x9f, 0x9e, 0x4b, 0xaf, 0xe4, 0xfe, 0x75, 0x33, 0x41, 0x61, 0xfc, 0xb3, 0x7c, 0x82, + 0xbf, 0xb0, 0x86, 0xab, 0xf6, 0xed, 0xcc, 0xf1, 0xec, 0xdb, 0xd9, 0xc1, 0xec, 0xdb, 0xb1, 0x6d, + 0x2a, 0x37, 0xc8, 0x36, 0xf5, 0x55, 0x98, 0xd8, 0xa4, 0x76, 0xcb, 0xdf, 0x74, 0x45, 0x02, 0x78, + 0xee, 0xe8, 0x2a, 0x13, 0xe2, 0xb0, 0x32, 0xa9, 0x0c, 0x86, 0x0e, 0x3b, 0x01, 0x23, 0x60, 0xa2, + 0x95, 0x67, 0x84, 0x37, 0x75, 0x0e, 0xaa, 0x86, 0x3f, 0xd4, 0x47, 0xc3, 0xaf, 0xc1, 0xb8, 0xa0, + 0x8b, 0x92, 0x0a, 0x46, 0xaa, 0x28, 0x2b, 0x42, 0xb8, 0xac, 0x3d, 0x7c, 0xa2, 0x2f, 0xac, 0x9d, + 0x6b, 0xa1, 0x1a, 0x13, 0x36, 0x04, 0xcb, 0xed, 0x46, 0xc7, 0x75, 0xda, 0x38, 0x04, 0x23, 0xd1, + 0x10, 0x50, 0x01, 0xe6, 0x43, 0xa0, 0x20, 0x91, 0xb7, 0x61, 0xb2, 0xb4, 0x51, 0x55, 0xc9, 0x0a, + 0x91, 0x89, 0xdd, 0xee, 0x38, 0x96, 0x46, 0x1a, 0xc3, 0xed, 0xa7, 0x95, 0x8d, 0xfe, 0x8b, 0xd1, + 0xca, 0x8c, 0x1f, 0x18, 0x95, 0x9f, 0xf7, 0x67, 0x6b, 0x0c, 0xd4, 0xcd, 0x7b, 0xb9, 0x63, 0x9a, + 0xf7, 0xf2, 0x47, 0x29, 0x27, 0x9a, 0xc6, 0x34, 0x74, 0x0c, 0x8d, 0x69, 0xf8, 0x89, 0x4d, 0x75, + 0x23, 0xc7, 0xd4, 0x81, 0x62, 0x5f, 0x5a, 0x61, 0x90, 0x2f, 0x2d, 0x55, 0x6f, 0x1a, 0x7d, 0x72, + 0xbd, 0x09, 0x8e, 0xad, 0x37, 0xd5, 0xa2, 0xb0, 0xb1, 0xb1, 0x23, 0xbd, 0x71, 0xcf, 0x89, 0xf3, + 0xca, 0x74, 0x7a, 0xca, 0x9e, 0x30, 0x80, 0xec, 0x73, 0xa5, 0x8c, 0x7d, 0x33, 0x5d, 0x19, 0xeb, + 0xbf, 0xdb, 0xfc, 0xff, 0x58, 0x1d, 0x33, 0x3c, 0x1c, 0xe5, 0x2d, 0xdb, 0x6b, 0xe3, 0x39, 0xf1, + 0x2a, 0x8c, 0xc8, 0xd4, 0x55, 0x99, 0xc8, 0xe4, 0x91, 0xcc, 0x59, 0x25, 0xb1, 0xd8, 0x91, 0x5e, + 0x12, 0xab, 0x69, 0xb8, 0xf7, 0x05, 0x4c, 0xcb, 0x0a, 0x24, 0x60, 0xc6, 0xdf, 0xcd, 0xcb, 0x2f, + 0x99, 0xe9, 0x72, 0xe2, 0xc9, 0xe6, 0xb2, 0x32, 0x73, 0x8a, 0x06, 0x17, 0x9b, 0x9b, 0x98, 0x73, + 0x9d, 0x4e, 0xf2, 0xa9, 0x92, 0x89, 0x45, 0x4f, 0x45, 0xe5, 0x06, 0x78, 0x2a, 0xea, 0x4d, 0xed, + 0x9d, 0xa5, 0x7c, 0xf4, 0xb0, 0x07, 0x5b, 0xdd, 0xfd, 0x5f, 0x58, 0xba, 0xa1, 0x3e, 0x88, 0x34, + 0x14, 0xe5, 0xd5, 0x40, 0xca, 0x3e, 0x4f, 0x21, 0x85, 0x2a, 0xe9, 0xf0, 0x71, 0xd2, 0xf4, 0x8d, + 0xfc, 0x4b, 0x4d, 0xd3, 0xb7, 0x0c, 0xa0, 0x3c, 0x93, 0xcb, 0x6f, 0x64, 0x5e, 0x62, 0xc3, 0x74, + 0xf4, 0x13, 0xb9, 0x0a, 0xa1, 0xf1, 0x7b, 0x04, 0xa6, 0x6b, 0xb5, 0xf5, 0x8a, 0x63, 0xef, 0xb6, + 0x5d, 0x3f, 0x70, 0xea, 0xd5, 0xf6, 0x8e, 0xcb, 0xf4, 0xb1, 0x50, 0x2a, 0x28, 0xf9, 0xd8, 0x22, + 0x89, 0x10, 0x16, 0x33, 0x7d, 0x7f, 0xd9, 0xf3, 0x5c, 0x4f, 0xd5, 0xf7, 0x29, 0x03, 0x98, 0x1c, + 0xce, 0x54, 0x9e, 0x5a, 0x97, 0xbf, 0x77, 0xca, 0x2f, 0xc9, 0x50, 0xe5, 0xf1, 0x39, 0xc8, 0x94, + 0x65, 0x84, 0x26, 0x17, 0xac, 0x50, 0x81, 0x4f, 0x69, 0xc9, 0xfe, 0xa2, 0x62, 0x2e, 0xf3, 0xc4, + 0x9e, 0x84, 0xc7, 0x99, 0x0e, 0xc2, 0xd5, 0x3b, 0xe9, 0xc4, 0x37, 0x70, 0x00, 0x27, 0xb4, 0xa8, + 0xa3, 0x41, 0xad, 0x7d, 0xaf, 0x08, 0x15, 0xcb, 0xc0, 0x20, 0xd7, 0x14, 0x93, 0x9f, 0xfa, 0x30, + 0x41, 0x6a, 0x0d, 0xe4, 0x47, 0x33, 0x70, 0x2e, 0xb5, 0x24, 0xfc, 0xba, 0xc7, 0xb4, 0x84, 0x8b, + 0x8a, 0xd0, 0xe0, 0x4f, 0x30, 0xf4, 0xaa, 0xda, 0x4a, 0x11, 0x05, 0xfd, 0x6b, 0x22, 0xbf, 0x9e, + 0x81, 0x53, 0x1a, 0x46, 0x28, 0xf3, 0xfc, 0x30, 0x20, 0x37, 0x75, 0x5d, 0x7f, 0xfc, 0x74, 0xd6, + 0xf5, 0x05, 0xbd, 0x2f, 0x91, 0x48, 0x56, 0xfb, 0xd0, 0xab, 0x85, 0xe4, 0x21, 0x4c, 0x63, 0x91, + 0xb4, 0x3c, 0xb2, 0x35, 0x2b, 0x0c, 0x96, 0xb3, 0x51, 0xb3, 0x79, 0x24, 0x1d, 0x3e, 0xa3, 0xb7, + 0xf8, 0xdd, 0xc3, 0x85, 0x09, 0x0d, 0x5d, 0xa6, 0x30, 0xb4, 0x22, 0xf3, 0xa5, 0xd3, 0xde, 0x71, + 0xd5, 0xfd, 0x32, 0x51, 0x05, 0xf9, 0x4f, 0x32, 0x30, 0xc7, 0xa0, 0xbc, 0x1b, 0x37, 0x3d, 0xb7, + 0x15, 0x96, 0x4b, 0xe7, 0x86, 0x1e, 0xc3, 0xd6, 0x7c, 0x3a, 0xc3, 0xf6, 0x12, 0x36, 0x99, 0xcb, + 0x04, 0x6b, 0xc7, 0x73, 0x5b, 0x51, 0xf3, 0xb5, 0x67, 0x60, 0x7b, 0x35, 0x92, 0xfc, 0x60, 0x06, + 0x4e, 0x6b, 0xe6, 0x12, 0x35, 0xd1, 0xb3, 0x88, 0x57, 0x9c, 0x09, 0x23, 0x99, 0xa3, 0xa2, 0xf2, + 0x15, 0xb1, 0xfe, 0x2f, 0x62, 0x0b, 0xa2, 0xdd, 0x02, 0xdb, 0x62, 0xb5, 0x38, 0x96, 0xd2, 0x84, + 0xde, 0xb5, 0x10, 0x07, 0xa6, 0xf1, 0x62, 0x51, 0x73, 0xc2, 0x99, 0xed, 0xed, 0x84, 0x13, 0x3e, + 0x77, 0x84, 0x59, 0x64, 0x7b, 0x7b, 0xe2, 0x24, 0xb9, 0x92, 0xbf, 0x04, 0xa7, 0x13, 0xc0, 0xf0, + 0x6b, 0x3b, 0xd1, 0xf3, 0x6b, 0x7b, 0xf5, 0xf1, 0xe1, 0xc2, 0xcb, 0x69, 0xb5, 0xa5, 0x7d, 0x69, + 0xbd, 0x6b, 0x20, 0x36, 0x40, 0x54, 0x28, 0x5e, 0x93, 0x4d, 0x5f, 0xa0, 0xaf, 0x8a, 0xf5, 0xa1, + 0xe0, 0x33, 0x59, 0xae, 0xb4, 0x41, 0xdd, 0xf2, 0x22, 0x24, 0x42, 0x61, 0x5c, 0xc9, 0xa0, 0x7b, + 0x80, 0xcf, 0xca, 0xf6, 0xac, 0xe4, 0xbb, 0x87, 0x0b, 0x1a, 0x36, 0xd3, 0x8b, 0xd5, 0xd4, 0xbc, + 0xaa, 0x5e, 0xac, 0x21, 0x92, 0x5f, 0xcd, 0xc0, 0x2c, 0x03, 0x44, 0x8b, 0x4a, 0x74, 0x6a, 0xae, + 0xdf, 0xaa, 0xdf, 0x7b, 0x3a, 0xab, 0xfe, 0x05, 0x6c, 0xa3, 0xba, 0xea, 0x13, 0x43, 0x92, 0xda, + 0x38, 0x5c, 0xed, 0xda, 0x1d, 0xb6, 0xb6, 0xda, 0x4f, 0x0f, 0xb0, 0xda, 0xf9, 0x04, 0x1c, 0xbd, + 0xda, 0x7b, 0xd6, 0x42, 0x36, 0x61, 0x5c, 0xa8, 0xc4, 0x7c, 0xc0, 0x9e, 0xd7, 0xb2, 0x6f, 0xaa, + 0x45, 0xfc, 0x9c, 0x22, 0x12, 0x0c, 0x27, 0x7a, 0xa8, 0x71, 0x21, 0x6d, 0x98, 0xe1, 0xbf, 0x75, + 0x03, 0xc5, 0x42, 0x4f, 0x03, 0xc5, 0x25, 0xd1, 0xa3, 0xf3, 0x82, 0x7f, 0xcc, 0x4e, 0xa1, 0x26, + 0x40, 0x48, 0x61, 0x4c, 0x3a, 0x40, 0x34, 0x30, 0xff, 0x68, 0xcf, 0xf7, 0x37, 0x4b, 0xbc, 0x2c, + 0xea, 0x5c, 0x88, 0xd7, 0x19, 0xff, 0x72, 0x53, 0x78, 0x13, 0x1b, 0xa6, 0x04, 0x94, 0x1d, 0x80, + 0x51, 0xc2, 0xbf, 0xa0, 0xa5, 0xa0, 0x88, 0x95, 0x72, 0xe3, 0xa6, 0xac, 0x09, 0x53, 0x84, 0xc4, + 0x04, 0x7a, 0x9c, 0x1f, 0x59, 0x87, 0xe9, 0x52, 0xa7, 0xd3, 0x74, 0x68, 0x03, 0x7b, 0xc9, 0x5f, + 0x06, 0x35, 0xa2, 0xd7, 0x20, 0x6c, 0x5e, 0x28, 0x54, 0xfc, 0xf8, 0xb3, 0xa0, 0x49, 0x5a, 0xe3, + 0x3b, 0x99, 0x44, 0xa3, 0xd9, 0xc9, 0x1d, 0x7f, 0x28, 0x51, 0xcd, 0x78, 0x72, 0xe7, 0x4d, 0x44, + 0x0b, 0x42, 0x84, 0xc0, 0x94, 0x25, 0x35, 0xb3, 0x51, 0x8e, 0x2b, 0x4b, 0xe2, 0x78, 0x19, 0x1d, + 0x28, 0x17, 0xa4, 0x73, 0x64, 0x2e, 0x52, 0xba, 0xd0, 0x39, 0x52, 0xb8, 0x44, 0x1a, 0x3f, 0x98, + 0xd5, 0x97, 0x1d, 0xb9, 0xa4, 0xe8, 0xed, 0x4a, 0x6e, 0x25, 0xa9, 0xb7, 0x2b, 0xda, 0xfa, 0xdf, + 0xc9, 0xc0, 0xcc, 0xba, 0xb7, 0x6b, 0xb7, 0x9d, 0x6f, 0xf3, 0x1c, 0x8d, 0x2e, 0xce, 0x4b, 0xff, + 0x87, 0x75, 0x9e, 0xd6, 0x03, 0x21, 0xae, 0x52, 0x31, 0x5b, 0x29, 0xb8, 0x64, 0xcc, 0xb4, 0xf6, + 0xa0, 0xbb, 0x39, 0x36, 0x4c, 0x79, 0xa7, 0x85, 0xa3, 0x73, 0xb8, 0xf1, 0xe3, 0x59, 0x18, 0x53, + 0x3e, 0x01, 0xf2, 0x45, 0x18, 0x57, 0xf9, 0xa8, 0x56, 0x1f, 0xb5, 0x5a, 0x53, 0xc3, 0x42, 0xb3, + 0x0f, 0xb5, 0x5b, 0x9a, 0xd9, 0x87, 0x2d, 0x74, 0x84, 0x1e, 0xf3, 0x68, 0xf3, 0x5e, 0xca, 0xd1, + 0xe6, 0x58, 0x4f, 0xc8, 0xbe, 0x9d, 0x3c, 0xe0, 0x0c, 0xfe, 0xe2, 0xab, 0xf1, 0x53, 0x19, 0x28, + 0xc6, 0x3f, 0xd2, 0xcf, 0x64, 0x54, 0x8e, 0x61, 0xe2, 0xff, 0xb1, 0x6c, 0x98, 0x3e, 0x5b, 0x06, + 0xd1, 0x3c, 0xab, 0x7e, 0x2c, 0xef, 0x68, 0xd6, 0xf7, 0x33, 0x7a, 0x3e, 0x18, 0x35, 0xfc, 0x34, + 0x3d, 0x09, 0x54, 0xfe, 0xe7, 0x7e, 0x61, 0xe1, 0x39, 0xe3, 0x03, 0x98, 0x8d, 0x0f, 0x07, 0x5a, + 0xe0, 0x4b, 0x30, 0xa5, 0xc3, 0xe3, 0xc9, 0xf7, 0xe3, 0x54, 0x66, 0x1c, 0xdf, 0xf8, 0xfd, 0x6c, + 0x9c, 0xb7, 0xf0, 0x69, 0x61, 0x42, 0xa7, 0x6d, 0x6f, 0x37, 0xc3, 0xe4, 0xdb, 0x5c, 0xe8, 0x70, + 0x90, 0x29, 0xcb, 0x8e, 0xf3, 0x1a, 0x45, 0x18, 0x0a, 0x92, 0x4b, 0x0f, 0x05, 0x21, 0x37, 0x62, + 0x8e, 0x61, 0x4a, 0xde, 0x82, 0x7d, 0xba, 0x6d, 0x45, 0xce, 0x61, 0x31, 0x7f, 0xb0, 0x25, 0x98, + 0xd5, 0x92, 0x70, 0x4a, 0xfa, 0xa1, 0xc8, 0xe0, 0x1a, 0x60, 0x01, 0x27, 0x4e, 0x45, 0x26, 0x2b, + 0x30, 0xc2, 0x9a, 0x79, 0xd7, 0xee, 0x08, 0xc3, 0x3a, 0x09, 0x03, 0xc3, 0x9a, 0xe1, 0x81, 0x4f, + 0x89, 0x0d, 0x6b, 0x52, 0xb6, 0xe5, 0x6b, 0x2f, 0x30, 0x73, 0x44, 0xe3, 0x9f, 0x67, 0xd8, 0xf7, + 0x5f, 0x7f, 0xf0, 0x39, 0x7b, 0xd2, 0x82, 0x75, 0xa9, 0x8f, 0xcb, 0xd5, 0x1f, 0x65, 0x79, 0xfe, + 0x74, 0xb1, 0x7c, 0xde, 0x84, 0xe1, 0x4d, 0xdb, 0xdb, 0xa5, 0x81, 0xc8, 0x2c, 0xae, 0x72, 0xe1, + 0x05, 0x51, 0x56, 0x85, 0x00, 0x7f, 0x9b, 0x82, 0x40, 0xb5, 0x85, 0x65, 0x07, 0xb2, 0x85, 0x29, + 0xe6, 0xd9, 0xdc, 0x53, 0x33, 0xcf, 0x7e, 0x4f, 0x98, 0x2a, 0xbd, 0x14, 0x0c, 0x90, 0xe3, 0xf1, + 0x7c, 0xfc, 0xa9, 0x81, 0x44, 0x36, 0xce, 0x88, 0x1d, 0xb9, 0xa1, 0x3e, 0x5e, 0xa0, 0x44, 0x57, + 0x1c, 0xf1, 0x4c, 0x81, 0xf1, 0x47, 0x39, 0x3e, 0xc6, 0x62, 0xa0, 0x2e, 0x6a, 0x91, 0x57, 0xf8, + 0x9d, 0x30, 0x41, 0xaf, 0x06, 0xc1, 0xa2, 0x37, 0xc6, 0x45, 0xc8, 0xb3, 0xb5, 0x29, 0x46, 0x13, + 0xf1, 0xd8, 0xfa, 0x55, 0xf1, 0x58, 0x39, 0xfb, 0x96, 0x71, 0x4f, 0x52, 0x9f, 0x8b, 0xc1, 0x6d, + 0x4b, 0xfd, 0x96, 0x11, 0x83, 0x5c, 0x82, 0xfc, 0x9a, 0xdb, 0x90, 0xb9, 0x44, 0x67, 0x31, 0xfe, + 0xd6, 0x6d, 0x28, 0x2c, 0xe7, 0x32, 0x26, 0x62, 0xb0, 0xbe, 0x86, 0xd9, 0xc7, 0xd5, 0xbe, 0xb6, + 0x76, 0x6c, 0x91, 0xf0, 0x4a, 0xed, 0x6b, 0x94, 0xa8, 0x7c, 0x19, 0x26, 0xf5, 0x07, 0x23, 0x85, + 0x43, 0x1a, 0x9a, 0x59, 0x63, 0xef, 0x4e, 0xaa, 0xd6, 0x71, 0x9d, 0x88, 0x94, 0x61, 0x42, 0xcb, + 0x61, 0x26, 0x6e, 0xb8, 0xd0, 0xbc, 0xa9, 0x67, 0x40, 0x53, 0xcd, 0x9b, 0x1a, 0x09, 0xdb, 0xcf, + 0x45, 0xfb, 0x95, 0x7b, 0xae, 0x44, 0xdb, 0x05, 0x0e, 0xb9, 0x0e, 0x05, 0x1e, 0xe8, 0x5a, 0xad, + 0xa8, 0xb7, 0x15, 0x3e, 0xc2, 0x62, 0x81, 0xe2, 0x12, 0x51, 0x09, 0x6c, 0xfc, 0x02, 0x14, 0x85, + 0x48, 0x8a, 0x9e, 0x66, 0x3c, 0x0b, 0xf9, 0xa5, 0x6a, 0xc5, 0x54, 0xc5, 0x48, 0xdd, 0x69, 0x78, + 0x26, 0x42, 0x8d, 0x9f, 0xce, 0xc0, 0xe9, 0x35, 0x1a, 0xec, 0xbb, 0xde, 0x03, 0x93, 0xfa, 0x81, + 0xe7, 0xf0, 0xd7, 0x86, 0xf0, 0x43, 0xfc, 0x22, 0x79, 0x1b, 0x86, 0xd0, 0x33, 0x2a, 0xb6, 0x33, + 0xc4, 0xeb, 0x28, 0x4f, 0x88, 0x05, 0x3c, 0x84, 0x6e, 0x56, 0x26, 0x27, 0x22, 0x6f, 0x42, 0xbe, + 0x42, 0xdb, 0x07, 0xb1, 0xf7, 0x56, 0x12, 0xc4, 0xa1, 0x40, 0x68, 0xd0, 0xf6, 0x81, 0x89, 0x24, + 0xc6, 0x4f, 0x65, 0xe1, 0x44, 0x4a, 0xb3, 0xee, 0x7f, 0xf1, 0x19, 0x95, 0x8a, 0x65, 0x4d, 0x2a, + 0xca, 0x4b, 0xca, 0x9e, 0x03, 0x9f, 0x2a, 0x24, 0xff, 0x66, 0x06, 0x4e, 0xe9, 0x0b, 0x54, 0xb8, + 0x42, 0xde, 0xbf, 0x4e, 0xde, 0x82, 0xe1, 0x15, 0x6a, 0x37, 0xa8, 0x7c, 0x8b, 0xe1, 0x44, 0x98, + 0x92, 0x86, 0x47, 0xf1, 0xf1, 0x42, 0xce, 0x36, 0x8a, 0xf9, 0xe0, 0x50, 0x52, 0x11, 0x8d, 0xe3, + 0xfa, 0xb8, 0x21, 0x23, 0x6a, 0xd3, 0xaa, 0xea, 0x73, 0xd5, 0xff, 0xdd, 0x0c, 0x9c, 0xe9, 0x43, + 0xc3, 0x26, 0x8e, 0x4d, 0xbd, 0x3a, 0x71, 0xb8, 0xa3, 0x22, 0x94, 0xbc, 0x0b, 0x53, 0x9b, 0x42, + 0x9f, 0x97, 0xd3, 0x91, 0x8d, 0xbe, 0x17, 0xa9, 0xea, 0x5b, 0x72, 0x5e, 0xe2, 0xc8, 0x5a, 0xa8, + 0x77, 0xae, 0x6f, 0xa8, 0xb7, 0x1a, 0x39, 0x9d, 0x1f, 0x34, 0x72, 0xfa, 0x83, 0xf8, 0x33, 0xeb, + 0x22, 0x81, 0x5d, 0x14, 0x37, 0x9e, 0xe9, 0x1d, 0x37, 0xde, 0x37, 0x4d, 0x96, 0xf1, 0xe3, 0x19, + 0x28, 0xea, 0xbc, 0x9f, 0x74, 0x3e, 0xdf, 0xd1, 0xe6, 0xf3, 0x4c, 0xfa, 0x7c, 0xf6, 0x9e, 0xc8, + 0xff, 0x2d, 0x13, 0xef, 0xec, 0x40, 0x33, 0x68, 0xc0, 0x70, 0xc5, 0x6d, 0xd9, 0x4e, 0x5b, 0x7d, + 0xe9, 0xb3, 0x81, 0x10, 0x53, 0x94, 0x0c, 0x16, 0x66, 0x7f, 0x1e, 0x86, 0xd6, 0xdc, 0x76, 0xa9, + 0x22, 0x3c, 0x05, 0x91, 0x4f, 0xdb, 0x6d, 0x5b, 0x76, 0xc3, 0xe4, 0x05, 0x64, 0x15, 0xa0, 0x56, + 0xf7, 0x28, 0x6d, 0xd7, 0x9c, 0x6f, 0xd3, 0x98, 0xa6, 0xc1, 0x46, 0xa8, 0xd9, 0x45, 0xc1, 0x82, + 0x77, 0x3c, 0x3e, 0x22, 0x5a, 0xbe, 0xf3, 0x6d, 0x55, 0xde, 0x2a, 0xf4, 0x06, 0x05, 0x88, 0x88, + 0xf0, 0xd9, 0x33, 0xa7, 0x21, 0x9e, 0xb2, 0x9d, 0x10, 0xcf, 0x9e, 0x31, 0x80, 0xf6, 0xec, 0x19, + 0x03, 0x30, 0xd1, 0xbe, 0x42, 0x9d, 0xdd, 0x3d, 0xee, 0x32, 0x32, 0xc1, 0x97, 0xea, 0x1e, 0x42, + 0x54, 0xd1, 0xce, 0x71, 0x8c, 0xdf, 0x1e, 0x82, 0xd3, 0x26, 0xdd, 0x75, 0x98, 0x9a, 0x7c, 0xcf, + 0x77, 0xda, 0xbb, 0x5a, 0x20, 0xb4, 0x11, 0x5b, 0x48, 0x22, 0x6b, 0x30, 0x83, 0x84, 0x03, 0x73, + 0x19, 0x0a, 0x6c, 0x57, 0x54, 0xd6, 0x12, 0xde, 0xa1, 0xe0, 0x3b, 0xdd, 0x7c, 0x91, 0xcb, 0x62, + 0xf2, 0x8a, 0xd8, 0xb5, 0x95, 0xbc, 0xee, 0x6c, 0xd7, 0xfe, 0xe4, 0x70, 0x01, 0x6a, 0x07, 0x7e, + 0x40, 0xf1, 0xc4, 0x26, 0x76, 0xee, 0x50, 0xb5, 0xce, 0xf7, 0x50, 0xad, 0xef, 0xc2, 0x6c, 0xa9, + 0xc1, 0x85, 0xb5, 0xdd, 0xdc, 0xf0, 0x9c, 0x76, 0xdd, 0xe9, 0xd8, 0x4d, 0x79, 0x5c, 0xc4, 0x51, + 0xb6, 0xc3, 0x72, 0xab, 0x13, 0x22, 0x98, 0xa9, 0x64, 0xac, 0x1b, 0x95, 0xb5, 0x1a, 0xc6, 0x0b, + 0x8b, 0xeb, 0x31, 0xec, 0x46, 0xa3, 0xed, 0x63, 0x2f, 0x7c, 0x33, 0x2c, 0x46, 0xa5, 0x1e, 0x7d, + 0x10, 0x36, 0x57, 0x6b, 0x51, 0x48, 0x11, 0x4f, 0x3b, 0xcb, 0xfd, 0x14, 0x82, 0xa6, 0x8f, 0xbe, + 0x0a, 0x1a, 0x5e, 0x44, 0x57, 0xab, 0xad, 0x30, 0xba, 0x42, 0x82, 0xce, 0xf7, 0xf7, 0x54, 0x3a, + 0x8e, 0x47, 0xae, 0xb2, 0xa5, 0xd0, 0x72, 0x03, 0x8a, 0xeb, 0x7c, 0x34, 0x3a, 0x02, 0x78, 0x08, + 0xe5, 0x47, 0x00, 0x05, 0x85, 0xbc, 0x0d, 0x33, 0xcb, 0x4b, 0x8b, 0xd2, 0xa8, 0x59, 0x71, 0xeb, + 0x5d, 0xbc, 0x55, 0x06, 0xac, 0x0f, 0xe7, 0x90, 0xd6, 0x17, 0xd9, 0xe2, 0x4e, 0x43, 0x23, 0x17, + 0x61, 0xa4, 0x5a, 0xe1, 0x63, 0x3f, 0xa6, 0xbe, 0xad, 0x20, 0xbc, 0x35, 0x64, 0x21, 0x59, 0x8f, + 0x74, 0xd4, 0xf1, 0x23, 0x95, 0xc9, 0xd3, 0x03, 0xe8, 0xa7, 0x6f, 0xc2, 0x44, 0xd9, 0x0d, 0xaa, + 0x6d, 0x3f, 0xb0, 0xdb, 0x75, 0x5a, 0xad, 0xa8, 0x89, 0x0e, 0xb7, 0xdd, 0xc0, 0x72, 0x44, 0x09, + 0x6b, 0xb9, 0x8e, 0x29, 0x5e, 0x6f, 0xe0, 0xaf, 0x04, 0x2d, 0xb9, 0x0d, 0xea, 0xdf, 0xbf, 0xf6, + 0x39, 0x7b, 0xbd, 0x41, 0xe9, 0x1b, 0x0a, 0xbe, 0x6b, 0xa9, 0x52, 0xf2, 0xdf, 0xc4, 0xd7, 0x1b, + 0x12, 0xb8, 0xe4, 0xcb, 0x30, 0x84, 0x3f, 0x85, 0xca, 0x34, 0x93, 0xc2, 0x36, 0x52, 0x97, 0xea, + 0xfc, 0x91, 0x5f, 0x24, 0x20, 0x55, 0x18, 0x11, 0xda, 0xfa, 0x71, 0x72, 0x90, 0x0b, 0xb5, 0x9f, + 0xcf, 0xaf, 0xa0, 0x37, 0x1a, 0x30, 0xae, 0x56, 0xc8, 0xd6, 0xf5, 0x8a, 0xed, 0xef, 0xd1, 0x06, + 0xfb, 0x25, 0x9e, 0x0f, 0xc1, 0x75, 0xbd, 0x87, 0x50, 0x8b, 0xb5, 0xc3, 0x54, 0x50, 0x98, 0xa0, + 0xae, 0xfa, 0xf7, 0x7c, 0xd1, 0x14, 0x71, 0x7e, 0x77, 0xd0, 0x16, 0xd4, 0x30, 0x45, 0x91, 0xf1, + 0x3d, 0x30, 0xbb, 0xd6, 0x6d, 0x36, 0xd9, 0x59, 0x5e, 0xa6, 0x97, 0x0e, 0xec, 0x80, 0x92, 0x32, + 0x0c, 0xd5, 0x94, 0x67, 0x03, 0x67, 0xc2, 0xfc, 0xdd, 0x11, 0x0e, 0xba, 0xb7, 0x65, 0x30, 0x02, + 0x3b, 0xf6, 0x60, 0x20, 0x27, 0x35, 0x7e, 0x37, 0x7a, 0x6e, 0x7a, 0xd3, 0xb3, 0xeb, 0x0f, 0xc2, + 0xa7, 0x23, 0x07, 0x7d, 0x39, 0xfb, 0xb6, 0x6c, 0x84, 0xbe, 0x0b, 0xa6, 0x35, 0xf8, 0xa8, 0xc6, + 0x90, 0xb7, 0x61, 0x4c, 0xec, 0x84, 0x4a, 0xde, 0x20, 0x4c, 0xce, 0x20, 0xdf, 0xae, 0x8f, 0x79, + 0x2a, 0xa8, 0xe8, 0xb8, 0xc1, 0xeb, 0x5d, 0xb9, 0x7f, 0xed, 0xb3, 0xd8, 0xe0, 0xf5, 0x3a, 0xfa, + 0x2c, 0xdd, 0xdf, 0x1c, 0x8b, 0x8f, 0xad, 0x58, 0xbb, 0x37, 0xd4, 0x4c, 0x21, 0x99, 0xe8, 0xb8, + 0x15, 0x65, 0x0a, 0x51, 0x8f, 0x5b, 0x21, 0x6a, 0x38, 0x27, 0xd9, 0x23, 0xe6, 0xe4, 0x5d, 0x39, + 0x27, 0xb9, 0xde, 0x0b, 0x63, 0xa6, 0xcf, 0x3c, 0xd4, 0xa2, 0x2f, 0x24, 0x3f, 0xd0, 0x59, 0xfd, + 0x39, 0x4c, 0x89, 0xca, 0x49, 0xe2, 0xb2, 0x50, 0x70, 0x52, 0x0d, 0x00, 0x43, 0x83, 0x33, 0x3d, + 0x42, 0xc0, 0x7e, 0x05, 0xc6, 0x4b, 0x41, 0x60, 0xd7, 0xf7, 0x68, 0xa3, 0xc2, 0xc4, 0x93, 0x92, + 0xd4, 0xc0, 0x16, 0x70, 0xf5, 0x26, 0x46, 0xc5, 0xe5, 0x49, 0xba, 0x6c, 0x5f, 0x38, 0xca, 0x85, + 0x49, 0xba, 0x18, 0x44, 0x4f, 0xd2, 0xc5, 0x20, 0xe4, 0x2a, 0x8c, 0x54, 0xdb, 0x0f, 0x1d, 0x36, + 0x26, 0x05, 0xe5, 0x81, 0x7c, 0x0e, 0x52, 0x85, 0xab, 0xc0, 0x22, 0x6f, 0x2a, 0x9a, 0xf2, 0x68, + 0x74, 0x2a, 0xe6, 0x76, 0x94, 0x30, 0x1e, 0x5a, 0xd5, 0x82, 0x43, 0xd5, 0xf9, 0x06, 0x8c, 0x48, + 0xf3, 0x18, 0x44, 0x27, 0x61, 0x41, 0x99, 0x0c, 0xbc, 0x94, 0xc8, 0xf8, 0xda, 0xa0, 0xf2, 0x0c, + 0xca, 0x98, 0xf2, 0xda, 0xa0, 0xf2, 0x0c, 0x8a, 0xf6, 0xda, 0xa0, 0xf2, 0x20, 0x4a, 0x68, 0x59, + 0x18, 0x3f, 0xd2, 0xb2, 0x70, 0x1f, 0xc6, 0x37, 0x6c, 0x2f, 0x70, 0x98, 0xa6, 0xd1, 0x0e, 0xfc, + 0xb9, 0x09, 0xcd, 0x18, 0xa7, 0x14, 0x95, 0x9f, 0x97, 0x0f, 0xe4, 0x75, 0x14, 0x7c, 0xfd, 0x25, + 0xb7, 0x08, 0x9e, 0xee, 0x26, 0x37, 0xf9, 0x24, 0x6e, 0x72, 0x38, 0xa8, 0x68, 0x80, 0x99, 0x8a, + 0x8e, 0xf9, 0xa8, 0x09, 0xc7, 0xac, 0x30, 0x21, 0x22, 0xf9, 0x3a, 0x8c, 0xb3, 0xbf, 0xf1, 0x4d, + 0x7a, 0x87, 0xfa, 0x73, 0x45, 0xec, 0xdc, 0xf3, 0xa9, 0x5f, 0x3f, 0x7f, 0xb8, 0xbe, 0x46, 0x03, + 0xfe, 0x01, 0x23, 0xe3, 0xb8, 0x65, 0x55, 0xe3, 0x46, 0xde, 0x83, 0x71, 0xb6, 0xfa, 0xb6, 0x6d, + 0x9f, 0x2b, 0x98, 0xd3, 0x91, 0xa3, 0x63, 0x43, 0xc0, 0x13, 0x79, 0xf2, 0x54, 0x02, 0xb6, 0xcd, + 0x97, 0x3a, 0x5c, 0x40, 0x12, 0x65, 0xb5, 0x77, 0x12, 0xc2, 0x51, 0xa2, 0x91, 0xf7, 0x61, 0xbc, + 0xd4, 0xe9, 0x44, 0x12, 0x67, 0x46, 0xb1, 0xae, 0x74, 0x3a, 0x56, 0xaa, 0xd4, 0xd1, 0x28, 0xe2, + 0x82, 0x79, 0xf6, 0x58, 0x82, 0x99, 0xbc, 0x1e, 0xea, 0xdc, 0x27, 0x22, 0x53, 0xa1, 0x38, 0x8d, + 0x68, 0x0a, 0x3c, 0x57, 0xbf, 0x97, 0x60, 0x82, 0xdb, 0xce, 0xa4, 0x36, 0x73, 0x32, 0xf1, 0xf5, + 0xa4, 0x28, 0x35, 0x3a, 0x0d, 0x59, 0x86, 0x49, 0x1e, 0x63, 0xd6, 0x14, 0x09, 0x0c, 0xe7, 0x4e, + 0x45, 0x2f, 0x1f, 0xf3, 0xd0, 0xb4, 0x26, 0xe6, 0xb5, 0xb6, 0x35, 0x2e, 0x31, 0x22, 0xe3, 0x8f, + 0x33, 0x70, 0xaa, 0xc7, 0x8c, 0x87, 0xe9, 0xed, 0x32, 0xfd, 0xd3, 0xdb, 0x31, 0xc9, 0xa1, 0x1f, + 0xb5, 0xb1, 0xff, 0x42, 0xcb, 0x52, 0xe7, 0x4b, 0xea, 0x5b, 0x2e, 0x10, 0x91, 0x3a, 0x5e, 0x54, + 0x7d, 0xdb, 0x45, 0x7b, 0x5f, 0x2e, 0xb9, 0x09, 0x09, 0x3c, 0xde, 0xa8, 0xb2, 0xf1, 0xf8, 0x70, + 0xe1, 0x79, 0x91, 0x99, 0x3e, 0x9c, 0xd6, 0x8f, 0x5d, 0xed, 0x0b, 0x4e, 0x61, 0x6d, 0x1c, 0x66, + 0x60, 0x4c, 0xf9, 0x0e, 0xc9, 0x79, 0x25, 0x62, 0xad, 0xc8, 0xdf, 0x36, 0x50, 0x38, 0x64, 0xf9, + 0x4e, 0x84, 0x1f, 0x55, 0xf6, 0x68, 0xab, 0xe6, 0x5d, 0xa6, 0x0a, 0x29, 0x29, 0x00, 0x5b, 0x9a, + 0x09, 0xd2, 0xc4, 0x72, 0x7c, 0xd7, 0xd3, 0xf6, 0x83, 0x52, 0x3d, 0x70, 0x1e, 0xd2, 0x01, 0x36, + 0x9d, 0xe8, 0x5d, 0x4f, 0xdb, 0x0f, 0x2c, 0x1b, 0xc9, 0x12, 0xef, 0x7a, 0x86, 0x0c, 0x8d, 0x1f, + 0xca, 0x00, 0xdc, 0xab, 0x2e, 0x61, 0x0e, 0xcf, 0x27, 0x55, 0x0a, 0xd2, 0xf3, 0xa2, 0x49, 0xee, + 0x7d, 0xd4, 0x81, 0xff, 0x2e, 0x03, 0x93, 0x3a, 0x1a, 0x79, 0x17, 0xa6, 0x6a, 0x75, 0xcf, 0x6d, + 0x36, 0xb7, 0xed, 0xfa, 0x83, 0x55, 0xa7, 0x4d, 0x79, 0x46, 0xaa, 0x21, 0xbe, 0x17, 0xf9, 0x61, + 0x91, 0xd5, 0x64, 0x65, 0x66, 0x1c, 0x99, 0xfc, 0x70, 0x06, 0x26, 0x6a, 0x7b, 0xee, 0x7e, 0xf4, + 0xdc, 0x3a, 0x9f, 0x90, 0x8f, 0xd8, 0xb7, 0xed, 0xef, 0xb9, 0xfb, 0x56, 0xca, 0x9b, 0xeb, 0x9f, + 0x1c, 0x2e, 0xbc, 0x33, 0xd8, 0x65, 0x6f, 0xdd, 0xc5, 0xf3, 0x48, 0xe0, 0x5f, 0xd1, 0x2a, 0x31, + 0xf5, 0x3a, 0x8d, 0x3f, 0xcb, 0xc0, 0x18, 0x9e, 0x5c, 0x9a, 0x4d, 0xd4, 0xb9, 0x3e, 0x4f, 0xaf, + 0xec, 0x84, 0xfd, 0xea, 0x33, 0xb1, 0x6f, 0xc0, 0x54, 0x0c, 0x8d, 0x18, 0x30, 0x5c, 0xc3, 0x28, + 0x65, 0xd5, 0xcc, 0xc0, 0xe3, 0x96, 0x4d, 0x51, 0x62, 0x2c, 0x2b, 0x64, 0xf7, 0xaf, 0xe1, 0x5d, + 0xe1, 0x22, 0x80, 0x23, 0x41, 0xf2, 0x64, 0x43, 0xe2, 0x2d, 0xb9, 0x7f, 0xcd, 0x54, 0xb0, 0x8c, + 0x35, 0x18, 0xae, 0xb9, 0x5e, 0x50, 0x3e, 0xe0, 0x87, 0x89, 0x0a, 0xf5, 0xeb, 0xea, 0x65, 0xa0, + 0x83, 0x06, 0xf8, 0xba, 0x29, 0x8a, 0xc8, 0x02, 0x0c, 0xdd, 0x74, 0x68, 0xb3, 0xa1, 0x7a, 0x7d, + 0xee, 0x30, 0x80, 0xc9, 0xe1, 0xec, 0xc0, 0x75, 0x32, 0x4a, 0x75, 0x1d, 0xb9, 0x97, 0x3e, 0xe9, + 0x77, 0xb3, 0xa4, 0x8d, 0xef, 0x0b, 0xfa, 0x23, 0xb6, 0x5a, 0x4d, 0x7d, 0x86, 0xfa, 0x3f, 0xc8, + 0xc0, 0x7c, 0x6f, 0x12, 0xd5, 0x63, 0x35, 0xd3, 0xc7, 0x63, 0xf5, 0xa5, 0xf8, 0xe5, 0x15, 0xa2, + 0x89, 0xcb, 0xab, 0xe8, 0xca, 0xaa, 0x82, 0x0e, 0xc3, 0xf5, 0xf0, 0x8d, 0xf1, 0xf3, 0x7d, 0xda, + 0x8c, 0x88, 0x7c, 0x9a, 0x03, 0xa4, 0x31, 0x05, 0xad, 0xf1, 0x1b, 0x79, 0x38, 0xdd, 0x93, 0x82, + 0xac, 0x28, 0x59, 0xf3, 0x27, 0xc3, 0x7c, 0xdd, 0x3d, 0xf1, 0xaf, 0xe0, 0xbf, 0xe8, 0x13, 0x16, + 0x8f, 0x63, 0x59, 0x0f, 0xb3, 0xa5, 0x67, 0x91, 0xd7, 0xab, 0x47, 0xf2, 0xe2, 0xe8, 0xc8, 0x0c, + 0x92, 0x89, 0xd3, 0x31, 0xe2, 0x89, 0x06, 0xb6, 0xd3, 0xf4, 0xd5, 0xcf, 0xae, 0xc1, 0x41, 0xa6, + 0x2c, 0x8b, 0xdc, 0x88, 0xf3, 0xe9, 0x6e, 0xc4, 0xc6, 0xff, 0x93, 0x81, 0xd1, 0xb0, 0xd9, 0x64, + 0x1e, 0x4e, 0x6e, 0x9a, 0xa5, 0xa5, 0x65, 0x6b, 0xf3, 0x83, 0x8d, 0x65, 0xeb, 0xde, 0x5a, 0x6d, + 0x63, 0x79, 0xa9, 0x7a, 0xb3, 0xba, 0x5c, 0x29, 0x3e, 0x47, 0xa6, 0x61, 0xe2, 0xde, 0xda, 0x9d, + 0xb5, 0xf5, 0xad, 0x35, 0x6b, 0xd9, 0x34, 0xd7, 0xcd, 0x62, 0x86, 0x4c, 0xc0, 0xa8, 0x59, 0x2e, + 0x2d, 0x59, 0x6b, 0xeb, 0x95, 0xe5, 0x62, 0x96, 0x14, 0x61, 0x7c, 0x69, 0x7d, 0x6d, 0x6d, 0x79, + 0x69, 0xb3, 0x7a, 0xbf, 0xba, 0xf9, 0x41, 0x31, 0x47, 0x08, 0x4c, 0x22, 0xc2, 0x86, 0x59, 0x5d, + 0x5b, 0xaa, 0x6e, 0x94, 0x56, 0x8b, 0x79, 0x06, 0x63, 0xf8, 0x0a, 0x6c, 0x28, 0x64, 0x74, 0xe7, + 0x5e, 0x79, 0xb9, 0x38, 0xcc, 0x50, 0xd8, 0x5f, 0x0a, 0xca, 0x08, 0xab, 0x1e, 0x51, 0x2a, 0xa5, + 0xcd, 0x52, 0xb9, 0x54, 0x5b, 0x2e, 0x16, 0xc8, 0x29, 0x98, 0xd1, 0x40, 0xd6, 0xea, 0xfa, 0xad, + 0xea, 0x5a, 0x71, 0x94, 0xcc, 0x42, 0x31, 0x84, 0x55, 0xca, 0xd6, 0xbd, 0xda, 0xb2, 0x59, 0x84, + 0x38, 0x74, 0xad, 0x74, 0x77, 0xb9, 0x38, 0x66, 0xbc, 0xc3, 0x23, 0x8c, 0xf8, 0x50, 0x93, 0x93, + 0x40, 0x6a, 0x9b, 0xa5, 0xcd, 0x7b, 0xb5, 0x58, 0xe7, 0xc7, 0x60, 0xa4, 0x76, 0x6f, 0x69, 0x69, + 0xb9, 0x56, 0x2b, 0x66, 0x08, 0xc0, 0xf0, 0xcd, 0x52, 0x75, 0x75, 0xb9, 0x52, 0xcc, 0x1a, 0x3f, + 0x99, 0x81, 0x69, 0xa9, 0x01, 0xca, 0x9b, 0x88, 0x27, 0xfc, 0x16, 0xdf, 0xd5, 0x0e, 0xb6, 0x32, + 0x00, 0x24, 0x56, 0x49, 0x9f, 0xcf, 0xd0, 0x83, 0x13, 0xa9, 0xc8, 0xe4, 0x03, 0x28, 0xca, 0x06, + 0xdc, 0xb5, 0x83, 0xfa, 0x5e, 0x24, 0xc6, 0x9e, 0x8f, 0x55, 0x12, 0x43, 0xe3, 0xb6, 0xc9, 0xe8, + 0x19, 0xbf, 0x04, 0x1b, 0xe3, 0xe7, 0x32, 0x70, 0xaa, 0x07, 0x31, 0x59, 0x82, 0xe1, 0x30, 0x89, + 0x78, 0x1f, 0x5f, 0xa7, 0xd9, 0xef, 0x1e, 0x2e, 0x08, 0x44, 0x7c, 0xcd, 0x0c, 0xff, 0x32, 0x87, + 0xc3, 0xac, 0xe0, 0x98, 0x9a, 0x9b, 0x8f, 0xc9, 0xe9, 0xd8, 0x70, 0x8a, 0x9a, 0x4a, 0x5b, 0xb5, + 0xf2, 0x98, 0x18, 0x90, 0x9c, 0xbd, 0xef, 0x63, 0x6e, 0x6e, 0xe3, 0xa7, 0x33, 0x4c, 0x63, 0x8b, + 0x23, 0x32, 0x45, 0xb6, 0xe4, 0xfb, 0xdd, 0x16, 0x35, 0xdd, 0x26, 0x2d, 0x99, 0x6b, 0x62, 0x2f, + 0x40, 0x15, 0xd4, 0xc6, 0x02, 0x3c, 0x2b, 0x58, 0xb6, 0xd7, 0xd6, 0xee, 0x35, 0x55, 0x1a, 0xf2, + 0x26, 0x40, 0xf8, 0xaa, 0xbc, 0x4c, 0x12, 0xc0, 0x93, 0x64, 0x08, 0xa8, 0xae, 0x44, 0x2b, 0xc8, + 0xc6, 0x5f, 0xc9, 0xc0, 0xb8, 0x38, 0x09, 0x95, 0x9a, 0xd4, 0x0b, 0x9e, 0x6c, 0xcd, 0xbc, 0xa9, + 0xad, 0x99, 0xd0, 0xb5, 0x5f, 0xe1, 0xcf, 0x8a, 0x53, 0x97, 0xcb, 0x3f, 0xca, 0x40, 0x31, 0x8e, + 0x48, 0xde, 0x85, 0x42, 0x8d, 0x3e, 0xa4, 0x9e, 0x13, 0x1c, 0x08, 0xe9, 0x27, 0x9f, 0x5b, 0xe1, + 0x38, 0xa2, 0x8c, 0xdb, 0x6a, 0x7d, 0xf1, 0xcb, 0x0c, 0x69, 0x06, 0x15, 0xe2, 0x8a, 0x2d, 0x23, + 0xf7, 0xb4, 0x6c, 0x19, 0xc6, 0xff, 0x9c, 0x85, 0x53, 0xb7, 0x68, 0xa0, 0xf6, 0x29, 0xbc, 0x88, + 0xfe, 0xc2, 0x60, 0xfd, 0x52, 0x7a, 0x32, 0x07, 0x23, 0x58, 0x24, 0xe7, 0xd7, 0x94, 0x3f, 0x49, + 0x39, 0x5c, 0xd7, 0x39, 0xed, 0x3d, 0x87, 0x1e, 0x75, 0x5f, 0x51, 0x32, 0xbc, 0x87, 0xcb, 0xfa, + 0x22, 0x4c, 0x62, 0x0a, 0xd3, 0x2e, 0xfb, 0x1c, 0x68, 0x43, 0xd8, 0x74, 0x0a, 0x66, 0x0c, 0x4a, + 0x5e, 0x81, 0x22, 0x83, 0x94, 0xea, 0x0f, 0xda, 0xee, 0x7e, 0x93, 0x36, 0x76, 0x29, 0x7f, 0x06, + 0xbc, 0x60, 0x26, 0xe0, 0x92, 0xe7, 0xbd, 0x36, 0x3f, 0x8f, 0xd1, 0x06, 0x1a, 0x5e, 0x04, 0xcf, + 0x08, 0x3a, 0xff, 0x26, 0x8c, 0x7d, 0xca, 0xd7, 0x1a, 0x8c, 0xff, 0x29, 0x03, 0xb3, 0xd8, 0x39, + 0xa5, 0x62, 0xf9, 0x92, 0x96, 0x1c, 0x2d, 0x25, 0x81, 0xb9, 0xcd, 0x40, 0xfa, 0xa7, 0x10, 0x8e, + 0x62, 0x64, 0xe8, 0xc9, 0x0e, 0x60, 0xe8, 0xa9, 0x1d, 0xe7, 0xd5, 0xd0, 0x01, 0xed, 0x54, 0xfc, + 0xad, 0xf7, 0x68, 0xca, 0x8d, 0x1f, 0xce, 0xc2, 0x88, 0x49, 0xf1, 0x39, 0x45, 0x72, 0x11, 0x46, + 0xd6, 0xdc, 0x80, 0xfa, 0x77, 0xb5, 0xb7, 0x33, 0xdb, 0x0c, 0x64, 0xb5, 0x1a, 0xa6, 0x2c, 0x64, + 0x0b, 0x7e, 0xc3, 0x73, 0x1b, 0xdd, 0x7a, 0xa0, 0x2e, 0xf8, 0x0e, 0x07, 0x99, 0xb2, 0x8c, 0xbc, + 0x06, 0xa3, 0x82, 0x73, 0x78, 0xfd, 0x87, 0x6e, 0xab, 0x1e, 0x0d, 0x9f, 0xe3, 0x8c, 0x10, 0x50, + 0x51, 0xe5, 0x5a, 0x43, 0x5e, 0x51, 0x54, 0x13, 0x8a, 0x80, 0xd4, 0xbf, 0x87, 0xfa, 0xe8, 0xdf, + 0x5f, 0x80, 0xe1, 0x92, 0xef, 0xd3, 0x40, 0x06, 0x3d, 0x8f, 0x87, 0x19, 0x68, 0x7c, 0x1a, 0x70, + 0xc6, 0x36, 0x96, 0x9b, 0x02, 0xcf, 0xf8, 0xe7, 0x59, 0x18, 0xc2, 0x3f, 0xf1, 0xca, 0xd3, 0xab, + 0xef, 0x69, 0x57, 0x9e, 0x5e, 0x7d, 0xcf, 0x44, 0x28, 0xb9, 0x86, 0xe6, 0x07, 0x99, 0x6b, 0x5f, + 0xf4, 0x1e, 0xed, 0xea, 0x8d, 0x08, 0x6c, 0xaa, 0x38, 0xe1, 0x5d, 0x70, 0x2e, 0x35, 0xd5, 0xc1, + 0x49, 0xc8, 0xae, 0xd7, 0x44, 0x8f, 0x31, 0x25, 0x8b, 0xeb, 0x9b, 0xd9, 0xf5, 0x1a, 0x8e, 0xc6, + 0x4a, 0x69, 0xf1, 0x8d, 0x1b, 0xea, 0x33, 0xaf, 0xfe, 0x9e, 0xbd, 0xf8, 0xc6, 0x0d, 0x53, 0x94, + 0xb0, 0xf1, 0xc5, 0x36, 0xe3, 0x9d, 0x28, 0x0f, 0xd2, 0xc5, 0xf1, 0xc5, 0xbe, 0xe1, 0xfd, 0xa7, + 0x19, 0x21, 0x90, 0x45, 0x18, 0x13, 0xa1, 0xe1, 0x88, 0xaf, 0x84, 0x6e, 0x8b, 0xd0, 0x71, 0x4e, + 0xa1, 0x22, 0xf1, 0xdb, 0x31, 0x31, 0x41, 0xf2, 0x45, 0x30, 0x71, 0x3b, 0x26, 0xa7, 0xd0, 0x37, + 0x15, 0x94, 0x28, 0xc6, 0x38, 0x0a, 0xbe, 0x55, 0x63, 0x8c, 0x31, 0x25, 0x6d, 0x88, 0x60, 0xfc, + 0x52, 0x16, 0x0a, 0x1b, 0xcd, 0xee, 0xae, 0xd3, 0xbe, 0x7f, 0x8d, 0x10, 0xc0, 0xb3, 0x99, 0xcc, + 0x59, 0xcc, 0xfe, 0x26, 0xa7, 0xa1, 0x20, 0x8f, 0x63, 0x52, 0x20, 0xf9, 0xe2, 0x28, 0x36, 0x07, + 0x72, 0xde, 0xc5, 0x9b, 0xf0, 0xf2, 0x27, 0xb9, 0x06, 0xe1, 0xa1, 0xaa, 0xd7, 0xe9, 0x2b, 0xcf, + 0x3e, 0x16, 0x33, 0x44, 0x23, 0xaf, 0x03, 0x6e, 0x12, 0xe2, 0x44, 0x20, 0xad, 0xd4, 0xbc, 0x69, + 0x42, 0xf9, 0xe0, 0x24, 0x88, 0x46, 0xae, 0x83, 0x58, 0x98, 0xe2, 0xe5, 0xc9, 0x13, 0x3a, 0x01, + 0x7f, 0xcb, 0x47, 0x92, 0x08, 0x54, 0xf2, 0x36, 0x8c, 0x45, 0x6f, 0xbe, 0x47, 0x0f, 0x4a, 0xaa, + 0x94, 0x4b, 0x51, 0xf9, 0xfd, 0x6b, 0xa6, 0x8a, 0x6e, 0xfc, 0x67, 0xc3, 0x30, 0xae, 0xb6, 0x87, + 0x98, 0x30, 0xe3, 0x37, 0xd9, 0x81, 0x5c, 0xb8, 0x25, 0x75, 0xb0, 0x50, 0x6c, 0xa7, 0xe7, 0xf5, + 0x06, 0x31, 0x3c, 0xee, 0xa3, 0x24, 0x63, 0xda, 0x57, 0x9e, 0x33, 0xa7, 0xfd, 0x08, 0xcc, 0xf1, + 0x48, 0x09, 0x0a, 0x6e, 0xc7, 0xdf, 0xa5, 0x6d, 0x47, 0x5e, 0xa2, 0x5c, 0xd0, 0x18, 0xad, 0x8b, + 0xc2, 0x04, 0xaf, 0x90, 0x8c, 0xbc, 0x01, 0xc3, 0x6e, 0x87, 0xb6, 0x6d, 0x47, 0xec, 0x71, 0x67, + 0x62, 0x0c, 0x68, 0xbb, 0x54, 0x55, 0x08, 0x05, 0x32, 0xb9, 0x0a, 0x79, 0xf7, 0x41, 0x38, 0x5f, + 0xa7, 0x75, 0xa2, 0x07, 0x81, 0xad, 0x90, 0x20, 0x22, 0x23, 0xf8, 0xd8, 0x6e, 0xed, 0x88, 0x19, + 0xd3, 0x09, 0x6e, 0xdb, 0xad, 0x1d, 0x95, 0x80, 0x21, 0x92, 0xf7, 0x00, 0x3a, 0xf6, 0x2e, 0xf5, + 0xac, 0x46, 0x37, 0x38, 0x10, 0xf3, 0xf6, 0xbc, 0x46, 0xb6, 0xc1, 0x8a, 0x2b, 0xdd, 0xe0, 0x40, + 0xa1, 0x1d, 0xed, 0x48, 0x20, 0x29, 0x01, 0xb4, 0xec, 0x20, 0xa0, 0x5e, 0xcb, 0x15, 0x7e, 0x61, + 0x63, 0xe1, 0x83, 0x8d, 0x9c, 0xc1, 0xdd, 0xb0, 0x58, 0xe1, 0xa0, 0x10, 0x61, 0xa3, 0x1d, 0xcf, + 0x16, 0xef, 0x7f, 0xc6, 0x1a, 0xed, 0x78, 0x5a, 0x2f, 0x19, 0x22, 0xf9, 0x32, 0x8c, 0x34, 0x1c, + 0xbf, 0xee, 0x7a, 0x0d, 0x91, 0xec, 0xe0, 0xac, 0x46, 0x53, 0xe1, 0x65, 0x0a, 0x99, 0x44, 0x67, + 0xad, 0x15, 0xf9, 0xd4, 0xd6, 0xdc, 0x7d, 0xb4, 0xdd, 0xc7, 0x5b, 0x5b, 0x0b, 0x8b, 0xd5, 0xd6, + 0x46, 0x44, 0x6c, 0x2a, 0x77, 0x9d, 0xa0, 0x69, 0x6f, 0x8b, 0x2b, 0x68, 0x7d, 0x2a, 0x6f, 0x61, + 0x91, 0x3a, 0x95, 0x1c, 0x99, 0xbc, 0x09, 0x05, 0xda, 0x0e, 0x3c, 0xdb, 0x72, 0x1a, 0x22, 0x9e, + 0x4e, 0x6f, 0x34, 0xdb, 0x80, 0xed, 0x6a, 0x45, 0x6d, 0x34, 0xe2, 0x57, 0x1b, 0x6c, 0x7c, 0xfc, + 0xba, 0xd3, 0x12, 0x61, 0x70, 0xfa, 0xf8, 0xd4, 0x96, 0xaa, 0x77, 0xd5, 0xf1, 0x61, 0x88, 0xe4, + 0x79, 0x80, 0x5d, 0xda, 0xa6, 0x3c, 0x26, 0x95, 0xdf, 0x32, 0x98, 0x0a, 0xe4, 0x2b, 0xf9, 0xff, + 0xf5, 0x17, 0x16, 0x32, 0x65, 0x80, 0x82, 0xcc, 0xf6, 0x60, 0xac, 0xc2, 0xe9, 0x9e, 0x1f, 0x05, + 0xb9, 0x0c, 0xc5, 0x1d, 0x5b, 0xd8, 0xb9, 0xea, 0x7b, 0x76, 0xbb, 0x4d, 0x9b, 0x42, 0x1c, 0x4d, + 0x49, 0xf8, 0x12, 0x07, 0x73, 0xce, 0xc6, 0x7b, 0x30, 0x9b, 0x36, 0x1a, 0xe4, 0x05, 0x18, 0x57, + 0x13, 0x5b, 0x08, 0x26, 0x63, 0x76, 0xc7, 0x91, 0xa9, 0x2d, 0x04, 0x83, 0x5f, 0xcf, 0xc0, 0xd9, + 0x7e, 0xdf, 0x16, 0x99, 0x87, 0x42, 0xc7, 0x73, 0x5c, 0xd4, 0xe1, 0xb8, 0x04, 0x0c, 0x7f, 0x93, + 0x73, 0x00, 0x5c, 0xd9, 0x08, 0xec, 0x5d, 0xe1, 0x27, 0x6f, 0x8e, 0x22, 0x64, 0xd3, 0xde, 0xf5, + 0xc9, 0xab, 0x30, 0xdd, 0xa0, 0x3b, 0x76, 0xb7, 0x19, 0x58, 0x7e, 0x7d, 0x8f, 0x36, 0x30, 0x34, + 0x05, 0xfd, 0x9f, 0xcc, 0xa2, 0x28, 0xa8, 0x49, 0x78, 0xa2, 0xc5, 0x43, 0x3d, 0x5a, 0x7c, 0x3b, + 0x5f, 0xc8, 0x14, 0xb3, 0x26, 0xba, 0x01, 0x19, 0x3f, 0x90, 0x85, 0xb9, 0x5e, 0x8b, 0x89, 0xbc, + 0x93, 0x36, 0x06, 0xdc, 0x54, 0xaf, 0xc2, 0x55, 0x53, 0xbd, 0x52, 0x1b, 0x59, 0x84, 0x30, 0xb0, + 0xe4, 0xa8, 0x20, 0x71, 0x09, 0x63, 0x34, 0x1d, 0xdb, 0xf7, 0xf7, 0xd9, 0xf7, 0x92, 0x53, 0x12, + 0xd7, 0x09, 0x98, 0x4a, 0x23, 0x61, 0xe4, 0x4b, 0x00, 0xf5, 0xa6, 0xeb, 0x53, 0xbc, 0x11, 0x17, + 0x1b, 0x31, 0xf7, 0xae, 0x0d, 0xa1, 0xea, 0x15, 0x28, 0x42, 0x97, 0xdc, 0x06, 0x15, 0x13, 0x68, + 0xc3, 0xa9, 0x1e, 0xd2, 0x83, 0x4d, 0x4f, 0xf4, 0xcc, 0xa5, 0x4c, 0x9a, 0xdf, 0x0d, 0x1f, 0xbb, + 0x8c, 0x8f, 0x78, 0xb6, 0xd7, 0x1a, 0x39, 0x00, 0x92, 0x14, 0x11, 0x8c, 0xbb, 0xf0, 0x11, 0xed, + 0x7a, 0x21, 0x77, 0x0e, 0xb9, 0xe7, 0x35, 0xc9, 0x02, 0x8c, 0xc9, 0x47, 0x71, 0x98, 0xa2, 0xcb, + 0x99, 0x83, 0x00, 0xdd, 0xa1, 0xb8, 0x78, 0x30, 0xbf, 0x22, 0x86, 0x0f, 0x89, 0x2d, 0x74, 0x14, + 0x21, 0x9b, 0x07, 0x1d, 0xd9, 0xbb, 0xb3, 0x72, 0x7d, 0xeb, 0x82, 0x5b, 0x94, 0xfe, 0x4c, 0x46, + 0x4e, 0x7f, 0x52, 0xf2, 0x1d, 0xd5, 0x3e, 0x02, 0x18, 0xec, 0x21, 0x1a, 0x86, 0x7f, 0xb3, 0x2d, + 0x5d, 0x7e, 0x75, 0x62, 0x4b, 0x17, 0x3f, 0xc9, 0x45, 0x98, 0xf2, 0xb8, 0x3b, 0x60, 0xe0, 0x8a, + 0xf1, 0xc4, 0x99, 0x32, 0x27, 0x38, 0x78, 0xd3, 0xc5, 0x31, 0x15, 0xed, 0xba, 0x1d, 0x0e, 0x98, + 0xb2, 0x11, 0x90, 0x2b, 0x30, 0xca, 0x36, 0x02, 0xcc, 0x1b, 0x11, 0xf3, 0x32, 0x47, 0x3c, 0xdc, + 0x56, 0xcd, 0xc2, 0xc7, 0xe2, 0x6f, 0xc1, 0xeb, 0xbf, 0xc8, 0x48, 0x66, 0xea, 0x36, 0x44, 0x4e, + 0xc1, 0x88, 0xeb, 0xed, 0x2a, 0x5d, 0x1b, 0x76, 0xbd, 0x5d, 0xd6, 0xaf, 0x4b, 0x50, 0xe4, 0x41, + 0x0f, 0x3c, 0x9a, 0xdc, 0x3f, 0x68, 0xf3, 0x73, 0x6a, 0xc1, 0x9c, 0xe4, 0x70, 0x7c, 0xf9, 0xf3, + 0xa0, 0x5d, 0x67, 0x98, 0xbe, 0xef, 0x5a, 0x6a, 0xb2, 0x18, 0xd1, 0xed, 0x49, 0xdf, 0x77, 0xa3, + 0xac, 0x31, 0x0d, 0x52, 0x86, 0x09, 0xc6, 0x27, 0x4c, 0x59, 0x23, 0x76, 0xc9, 0x73, 0xc9, 0x5d, + 0xf2, 0xa0, 0x5d, 0x97, 0x4d, 0x34, 0xc7, 0x7d, 0xe5, 0x97, 0xe8, 0xcd, 0xcf, 0x66, 0xe1, 0x64, + 0x3a, 0x3a, 0xce, 0x17, 0xab, 0x04, 0x63, 0x7f, 0xb8, 0xcd, 0xd2, 0x1c, 0x65, 0x10, 0x9e, 0xde, + 0x20, 0xad, 0xb5, 0xd9, 0xd4, 0xd6, 0xbe, 0x02, 0xd3, 0xc8, 0x48, 0xe8, 0x25, 0x4d, 0xc7, 0x0f, + 0x44, 0xd4, 0xbe, 0x39, 0xc5, 0x0a, 0xb8, 0x80, 0x5b, 0x65, 0x60, 0xf2, 0x12, 0x4c, 0x4a, 0x11, + 0xe5, 0xee, 0xb7, 0x59, 0xc5, 0x5c, 0x3e, 0x4d, 0x08, 0xe8, 0x3a, 0x02, 0xc9, 0x09, 0x18, 0xb6, + 0x3b, 0x1d, 0x56, 0x25, 0x17, 0x4b, 0x43, 0x76, 0xa7, 0x53, 0x6d, 0x90, 0x0b, 0x30, 0x81, 0x91, + 0x4e, 0xd6, 0x0e, 0x3a, 0x8a, 0x08, 0xdf, 0x32, 0x73, 0x1c, 0x81, 0xdc, 0x79, 0xc4, 0x67, 0x1f, + 0x02, 0xa3, 0x95, 0x28, 0x23, 0x88, 0x02, 0x76, 0x47, 0x22, 0x88, 0x91, 0xf9, 0x32, 0x4c, 0x89, + 0xdd, 0x54, 0x48, 0x78, 0xa4, 0x14, 0xeb, 0x8f, 0xa9, 0xb9, 0x22, 0x5b, 0x38, 0x08, 0x50, 0xb5, + 0x21, 0x29, 0xff, 0x20, 0x03, 0x27, 0x52, 0xb7, 0x63, 0xf2, 0x4d, 0xe0, 0x81, 0x1f, 0x81, 0x6b, + 0x79, 0xb4, 0xee, 0x74, 0x1c, 0x0c, 0x8d, 0xe7, 0x46, 0xa8, 0xc5, 0x7e, 0x1b, 0x39, 0x06, 0x91, + 0x6c, 0xba, 0x66, 0x48, 0xc4, 0xcf, 0xd1, 0x45, 0x2f, 0x06, 0x9e, 0xff, 0x10, 0x4e, 0xa4, 0xa2, + 0xa6, 0x9c, 0x6f, 0x5f, 0xd3, 0x1f, 0x55, 0x93, 0xb7, 0x0a, 0xb1, 0x4e, 0x2b, 0xe7, 0x5e, 0xd1, + 0xbd, 0xdf, 0x0a, 0xbb, 0x17, 0xdb, 0xb8, 0xc9, 0x72, 0x7c, 0x59, 0xa6, 0xe9, 0x9e, 0x92, 0xa8, + 0xe7, 0xca, 0x24, 0x1f, 0xc2, 0x09, 0xb1, 0x54, 0x76, 0x3d, 0xbb, 0xb3, 0x17, 0xb1, 0xe3, 0x0d, + 0x7d, 0x39, 0x8d, 0x1d, 0x5f, 0x43, 0xb7, 0x18, 0x7e, 0xc8, 0x75, 0xc6, 0x4e, 0x02, 0x45, 0x1f, + 0x3c, 0xb9, 0xe9, 0xa7, 0xb4, 0x26, 0x65, 0x0d, 0x66, 0xd2, 0xd6, 0xe0, 0xc0, 0x1f, 0x80, 0xa8, + 0xf3, 0x07, 0x33, 0x70, 0xfe, 0xa8, 0x36, 0x93, 0x2d, 0x38, 0x89, 0xf7, 0xde, 0xbe, 0x1b, 0x76, + 0xdb, 0xaa, 0xdb, 0xf5, 0x3d, 0x2a, 0x56, 0x89, 0x91, 0xda, 0xf9, 0x4e, 0xa7, 0x56, 0x5b, 0x57, + 0xfa, 0xdd, 0xe9, 0xd4, 0x7c, 0x57, 0xfe, 0x5e, 0x62, 0xe4, 0xa2, 0x0d, 0x0d, 0x38, 0xd3, 0x87, + 0x52, 0xf9, 0xac, 0x32, 0xea, 0x67, 0x75, 0x09, 0x8a, 0x3b, 0xb4, 0xc1, 0x54, 0x28, 0xda, 0xc0, + 0xa6, 0x3d, 0x5c, 0xe4, 0x2f, 0x13, 0x9a, 0x93, 0x21, 0xbc, 0xe6, 0xbb, 0xf7, 0x17, 0x45, 0x2d, + 0x2d, 0x29, 0x21, 0x55, 0x15, 0x8d, 0x5c, 0x81, 0x99, 0x58, 0x98, 0x7f, 0x14, 0x37, 0x6a, 0x4e, + 0xb3, 0x22, 0x3d, 0x29, 0xcc, 0x0b, 0x30, 0x2e, 0xa7, 0xc1, 0x0b, 0xa3, 0x4f, 0xcc, 0x31, 0x01, + 0x63, 0xab, 0x5c, 0x54, 0xf7, 0x77, 0xb3, 0x52, 0x65, 0x2a, 0xbb, 0x6e, 0xe0, 0x07, 0x9e, 0xdd, + 0xd1, 0xce, 0x4d, 0xa4, 0x05, 0xa7, 0x5d, 0xbb, 0x1b, 0xec, 0x2d, 0x5a, 0xec, 0x5f, 0xd7, 0x93, + 0xa1, 0xa0, 0x75, 0xe9, 0x09, 0x37, 0xb6, 0x78, 0x55, 0x17, 0x9d, 0x25, 0x86, 0x5d, 0x52, 0x91, + 0xd9, 0x0e, 0xaf, 0x70, 0x5d, 0x79, 0xce, 0x3c, 0xc5, 0x79, 0x26, 0xb0, 0xc8, 0x0a, 0x8c, 0x6f, + 0x53, 0xdb, 0xa3, 0x9e, 0x15, 0x3d, 0x81, 0x1e, 0x3f, 0x38, 0x95, 0x11, 0x01, 0x5d, 0x3b, 0x75, + 0xae, 0x63, 0xdb, 0x51, 0x09, 0x79, 0x17, 0x46, 0x9d, 0x86, 0x48, 0x3d, 0x27, 0x8e, 0x4f, 0xba, + 0xca, 0x5e, 0x6d, 0xf0, 0x4c, 0x74, 0x11, 0x0f, 0x76, 0xf6, 0x72, 0x04, 0xb4, 0x3c, 0xa1, 0x9d, + 0x30, 0x8d, 0xb2, 0xdc, 0x9d, 0x93, 0x64, 0x89, 0x07, 0xdc, 0x4f, 0xc2, 0xb0, 0xaf, 0xe4, 0xc2, + 0x33, 0xc5, 0x2f, 0xe3, 0xfb, 0xe0, 0xd2, 0xa0, 0x63, 0x44, 0x5e, 0x07, 0xd2, 0x63, 0xc0, 0x47, + 0xcd, 0x69, 0x3b, 0x31, 0x6e, 0x2f, 0x80, 0x9a, 0xcc, 0xcb, 0x91, 0x13, 0x2e, 0x61, 0xf7, 0x3c, + 0xc7, 0xf8, 0xa1, 0x1c, 0x4c, 0xea, 0x67, 0x6a, 0xf2, 0x2a, 0xe4, 0x43, 0xb6, 0x93, 0xa1, 0xed, + 0x57, 0x45, 0x62, 0xcc, 0x4d, 0x44, 0x62, 0x1b, 0x04, 0xde, 0xff, 0x58, 0x2d, 0xd5, 0x3c, 0x6b, + 0x8e, 0x23, 0x50, 0x9a, 0x65, 0x6f, 0x03, 0x7f, 0x08, 0x17, 0x65, 0x59, 0x30, 0xd8, 0xb3, 0xef, + 0x05, 0x76, 0xb2, 0x47, 0xbb, 0xda, 0x38, 0xa3, 0x65, 0xf2, 0x04, 0x5f, 0x7a, 0x8f, 0x8e, 0x4c, + 0xf9, 0xde, 0x47, 0x26, 0xd1, 0x95, 0x1e, 0x47, 0xa6, 0xa1, 0x3e, 0x47, 0xa6, 0x88, 0x52, 0x3d, + 0x32, 0xe1, 0xc1, 0x79, 0xa4, 0xd7, 0xc1, 0x39, 0xa2, 0xe1, 0x07, 0xe7, 0x17, 0x45, 0x77, 0x3d, + 0x7b, 0xdf, 0xc2, 0x71, 0xe0, 0x9e, 0x6a, 0xbc, 0x23, 0xa6, 0xbd, 0x8f, 0x37, 0x65, 0xe5, 0x51, + 0x90, 0xd7, 0x6b, 0xc6, 0x5f, 0xcf, 0xc4, 0x0e, 0x39, 0x72, 0x2a, 0x5e, 0x82, 0x49, 0xa7, 0xc5, + 0xb4, 0x2f, 0xda, 0x50, 0xb4, 0x86, 0x09, 0x73, 0x42, 0x42, 0xb9, 0xe6, 0xf0, 0x32, 0x4c, 0x85, + 0x68, 0x3c, 0x30, 0x99, 0x3b, 0xcf, 0x9b, 0x21, 0xb5, 0x08, 0x4c, 0x7e, 0x15, 0xa6, 0x43, 0x44, + 0xa1, 0xa8, 0x72, 0xc5, 0x61, 0xc2, 0x2c, 0xca, 0x02, 0xf1, 0xa4, 0xa3, 0x6f, 0xec, 0xc6, 0x77, + 0xa5, 0xcf, 0xa8, 0x55, 0xc6, 0x6f, 0xe5, 0x34, 0x05, 0x50, 0x56, 0x53, 0x86, 0x31, 0x26, 0x1a, + 0xc5, 0x20, 0x09, 0xb1, 0xf2, 0x42, 0x8f, 0xe1, 0x17, 0x17, 0x94, 0xb5, 0xda, 0xba, 0x09, 0xbe, + 0xef, 0xca, 0xfb, 0x4a, 0x8b, 0x4b, 0x7f, 0xae, 0xc3, 0xe0, 0xf2, 0x93, 0xec, 0xb8, 0x0c, 0x79, + 0xa5, 0x3f, 0xbb, 0x52, 0xa7, 0x83, 0x6d, 0x64, 0xab, 0x0f, 0x77, 0x81, 0xf0, 0x97, 0xac, 0xe0, + 0x1e, 0xe0, 0x79, 0xc9, 0xd7, 0x99, 0xe7, 0x52, 0xf6, 0xd5, 0x04, 0x73, 0x1c, 0x25, 0xe4, 0x8c, + 0x0f, 0xef, 0xfb, 0x2a, 0xdb, 0x65, 0x18, 0x67, 0xa7, 0xef, 0x90, 0x61, 0x5e, 0x8b, 0x22, 0xea, + 0xd5, 0xf9, 0xa5, 0xea, 0x5d, 0x73, 0x8c, 0xd1, 0x49, 0x36, 0x7b, 0x70, 0x5a, 0xd5, 0x11, 0xf5, + 0x46, 0x0e, 0xc9, 0x2c, 0x6d, 0x7d, 0x47, 0x20, 0x52, 0x25, 0xb1, 0xa9, 0x27, 0x6d, 0x1d, 0x20, + 0xd0, 0x8c, 0x3d, 0x98, 0xef, 0x3d, 0x25, 0xec, 0xd8, 0x41, 0xd5, 0xd0, 0x65, 0x53, 0xfe, 0x54, + 0x36, 0xc8, 0xac, 0xba, 0x41, 0x9e, 0x86, 0x82, 0x74, 0x72, 0x93, 0x07, 0x15, 0x9b, 0x3b, 0xb8, + 0x19, 0x7f, 0x3b, 0x07, 0x17, 0x06, 0x98, 0xae, 0x3e, 0x75, 0xbe, 0x0f, 0x63, 0xdc, 0x60, 0xc8, + 0xc5, 0x27, 0xbf, 0x41, 0x97, 0xdb, 0x00, 0x63, 0x2a, 0x64, 0x1d, 0x53, 0x61, 0x22, 0x79, 0x07, + 0x7e, 0xf8, 0x37, 0xf9, 0x26, 0x4c, 0x71, 0x81, 0xc6, 0x7d, 0x0c, 0x76, 0xba, 0xcd, 0x01, 0x24, + 0xda, 0x19, 0xe9, 0x10, 0x1d, 0x23, 0x45, 0x21, 0x87, 0x12, 0xa3, 0x16, 0xc2, 0xc8, 0x26, 0x8c, + 0x21, 0xda, 0x8e, 0xed, 0x34, 0x07, 0xf2, 0xcc, 0x95, 0xee, 0xd6, 0x2a, 0x19, 0x77, 0x8d, 0x62, + 0x80, 0x9b, 0xf8, 0x9b, 0x1d, 0xf2, 0xda, 0xdd, 0x96, 0x65, 0x77, 0x3a, 0x7c, 0x2d, 0x88, 0x5b, + 0x9f, 0x21, 0x73, 0xa2, 0xdd, 0x6d, 0x95, 0x3a, 0x1d, 0x9c, 0x52, 0xbc, 0x1e, 0x9a, 0x66, 0x78, + 0xfc, 0xab, 0x95, 0x98, 0xc3, 0x88, 0xc9, 0x18, 0xf0, 0xef, 0x56, 0xe0, 0xce, 0x02, 0xf7, 0x00, + 0xe0, 0xe6, 0x6e, 0x93, 0xff, 0x30, 0xfe, 0x34, 0x2b, 0x75, 0xb3, 0xde, 0xeb, 0xfe, 0x2f, 0xa6, + 0x28, 0x65, 0x8a, 0x2e, 0x41, 0x91, 0x0d, 0x7d, 0x24, 0x54, 0xc2, 0x39, 0x9a, 0x6c, 0x77, 0x5b, + 0xe1, 0xd8, 0xa9, 0x03, 0x3f, 0xac, 0x0e, 0xfc, 0x97, 0xa4, 0x42, 0x9a, 0x2a, 0x1e, 0x7a, 0x0f, + 0xb9, 0xf1, 0x4f, 0x73, 0x70, 0x71, 0x30, 0x21, 0xf0, 0x17, 0xf3, 0x96, 0x32, 0x6f, 0xb1, 0x43, + 0xf0, 0x50, 0xfc, 0x10, 0x9c, 0xf6, 0xed, 0x0d, 0xa7, 0x7d, 0x7b, 0x89, 0x23, 0xf7, 0x48, 0xca, + 0x91, 0x3b, 0xf5, 0x03, 0x2d, 0x1c, 0xf1, 0x81, 0x8e, 0xaa, 0xeb, 0xe4, 0x9f, 0x64, 0x61, 0x26, + 0xe5, 0x4a, 0x84, 0x7c, 0x08, 0x33, 0x52, 0xb5, 0xe7, 0x3b, 0x07, 0x57, 0xb9, 0xf9, 0xee, 0x7b, + 0x39, 0x4d, 0xa9, 0x47, 0xb4, 0x14, 0xc5, 0x7b, 0x5a, 0xa8, 0xf3, 0x51, 0xf9, 0x9f, 0x1f, 0x45, + 0x9e, 0x7c, 0x00, 0x27, 0x31, 0x8d, 0x68, 0xdd, 0x52, 0xf4, 0x79, 0xcb, 0xa3, 0x3b, 0x62, 0x3d, + 0xbc, 0x90, 0x50, 0x7b, 0x9d, 0xba, 0xd2, 0x1c, 0x93, 0xee, 0xac, 0x3c, 0x67, 0xce, 0xfa, 0x29, + 0xf0, 0xf8, 0x19, 0xe1, 0xdf, 0xcf, 0x80, 0x71, 0xf4, 0x78, 0xa1, 0xad, 0x32, 0x3e, 0xe0, 0xa3, + 0xe6, 0x98, 0xad, 0x8c, 0xde, 0x05, 0x98, 0xf0, 0xe8, 0x8e, 0x47, 0xfd, 0x3d, 0x65, 0xf8, 0x46, + 0xcd, 0x71, 0x01, 0x94, 0x03, 0x23, 0xf3, 0x10, 0x1d, 0x4b, 0xc9, 0x96, 0x44, 0xc6, 0xcd, 0xf0, + 0xe8, 0x97, 0x3a, 0x0f, 0x6c, 0x35, 0xa9, 0x0d, 0xe4, 0x3f, 0x6e, 0xe7, 0x0b, 0xd9, 0x62, 0xce, + 0x14, 0xd9, 0x92, 0x76, 0x9c, 0x26, 0x35, 0x7e, 0x2d, 0x23, 0x35, 0x82, 0xb4, 0xc1, 0x23, 0x1f, + 0x2a, 0x4e, 0x3c, 0xb9, 0x84, 0x1a, 0x92, 0x46, 0xa2, 0xfa, 0x3b, 0x88, 0x04, 0x3e, 0xf1, 0x97, + 0xff, 0x05, 0xcb, 0x27, 0xf1, 0x44, 0x78, 0x53, 0xde, 0x01, 0x32, 0x69, 0x77, 0xff, 0x1a, 0xb9, + 0x0c, 0x23, 0xfc, 0xda, 0x4f, 0x36, 0x74, 0x4a, 0x6b, 0xe8, 0xfd, 0x6b, 0xa6, 0x2c, 0x37, 0x7e, + 0x2e, 0x13, 0x5e, 0x7c, 0xc4, 0x9b, 0x7f, 0xff, 0x1a, 0xf9, 0xd2, 0x60, 0xee, 0x38, 0x05, 0xe9, + 0x8e, 0x13, 0xba, 0xe2, 0x7c, 0x59, 0x73, 0xc5, 0x79, 0xb1, 0xff, 0x38, 0x09, 0x13, 0x6b, 0xfc, + 0x6d, 0xe6, 0x3f, 0xcd, 0xc0, 0xb9, 0xbe, 0x14, 0xe4, 0x2c, 0x14, 0x4a, 0x1b, 0xd5, 0xcd, 0x68, + 0x66, 0xd9, 0xd7, 0x22, 0x21, 0xe4, 0x16, 0x8c, 0x96, 0x6d, 0xdf, 0xa9, 0xb3, 0x05, 0x9c, 0x6a, + 0x34, 0x4a, 0xb0, 0x0d, 0xd1, 0x57, 0x9e, 0x33, 0x23, 0x5a, 0x62, 0xc1, 0x34, 0x7e, 0x05, 0x89, + 0xb7, 0x4f, 0xe3, 0x06, 0x83, 0x04, 0xc3, 0x04, 0x19, 0x93, 0x30, 0x09, 0x60, 0xfc, 0xe3, 0x7b, + 0x28, 0xb5, 0x90, 0xde, 0x0d, 0x3c, 0x46, 0xce, 0xad, 0x4b, 0x50, 0xd8, 0x90, 0x97, 0x1f, 0xca, + 0xd3, 0xe7, 0xf2, 0xa2, 0xc3, 0x0c, 0x4b, 0x8d, 0xbf, 0x96, 0x91, 0xa7, 0xfa, 0xa3, 0x3b, 0xa2, + 0xa4, 0xd5, 0x6f, 0xf4, 0x4f, 0xab, 0xdf, 0xf8, 0x94, 0x69, 0xf5, 0x8d, 0x5f, 0x12, 0x19, 0x31, + 0xab, 0x8d, 0x8d, 0xd8, 0x4b, 0x4f, 0x4f, 0xea, 0x5c, 0xb8, 0xac, 0xad, 0xce, 0x0b, 0xca, 0x53, + 0x1f, 0xc9, 0xba, 0x7a, 0xfb, 0x18, 0x2a, 0x4b, 0xf5, 0x9f, 0x64, 0xe1, 0x6c, 0x3f, 0xf2, 0xd4, + 0x47, 0xa9, 0x32, 0xc7, 0x7b, 0x94, 0xea, 0x32, 0x14, 0x38, 0x4c, 0x7f, 0xa7, 0x57, 0x90, 0xb2, + 0x01, 0x97, 0xc5, 0xe4, 0x02, 0x0c, 0x97, 0x96, 0x6a, 0xd1, 0x5b, 0x04, 0xe8, 0x0d, 0x63, 0xd7, + 0x7d, 0xf4, 0xb3, 0x10, 0x45, 0xe4, 0x1b, 0xc9, 0xe7, 0x37, 0xc4, 0x23, 0x04, 0x67, 0x94, 0x01, + 0x49, 0x24, 0xab, 0xc5, 0xf6, 0x46, 0xc9, 0x55, 0x45, 0xbe, 0x42, 0x33, 0xf9, 0x94, 0x87, 0x01, + 0xc3, 0x1b, 0x1e, 0xf5, 0x69, 0xa0, 0x7a, 0xaa, 0x74, 0x10, 0x62, 0x8a, 0x12, 0xe1, 0x47, 0x62, + 0x1f, 0xf0, 0x58, 0xc0, 0x61, 0x35, 0xca, 0x1a, 0x1d, 0x4f, 0x18, 0xd8, 0x54, 0x50, 0x8c, 0xef, + 0x64, 0x60, 0x36, 0xad, 0x59, 0xe4, 0x2c, 0xe4, 0xdb, 0xa9, 0x0f, 0x87, 0xb4, 0x79, 0x44, 0xd2, + 0x18, 0xbe, 0x91, 0xba, 0xe3, 0x7a, 0x2d, 0x3b, 0x50, 0xdd, 0x73, 0x14, 0xb0, 0x09, 0xec, 0xc7, + 0x4d, 0xfc, 0x9b, 0x2c, 0x48, 0x61, 0x9b, 0x4b, 0x3c, 0x35, 0x82, 0xff, 0x19, 0x25, 0x80, 0x6a, + 0x63, 0x63, 0xbd, 0xc3, 0x73, 0x9f, 0x5e, 0x87, 0x3c, 0x6b, 0x56, 0x6c, 0x31, 0xb2, 0xe5, 0x50, + 0xba, 0xbb, 0x2a, 0x90, 0x78, 0xab, 0x7c, 0xbb, 0xd5, 0x34, 0x11, 0xd9, 0xd8, 0x82, 0x49, 0x1d, + 0x83, 0x2c, 0xeb, 0xd9, 0xb2, 0xc6, 0x16, 0x8b, 0x82, 0x53, 0xd9, 0x75, 0xb9, 0x8b, 0x68, 0xf9, + 0xf4, 0x77, 0x0f, 0x17, 0x80, 0xfd, 0xe4, 0x34, 0x69, 0xd9, 0xb4, 0x8c, 0x9f, 0xc8, 0xc2, 0x6c, + 0x14, 0x6a, 0x26, 0x3f, 0x89, 0x67, 0x36, 0xee, 0xa1, 0xa4, 0xf9, 0xe5, 0x4b, 0x8d, 0x29, 0xd9, + 0xc1, 0x3e, 0xee, 0xc0, 0xb7, 0x60, 0xae, 0x17, 0x3e, 0x79, 0x35, 0xf1, 0x86, 0xb9, 0xc8, 0xa6, + 0x10, 0x3e, 0x76, 0xae, 0x3c, 0x69, 0xfe, 0x0f, 0x33, 0x30, 0x2f, 0x1c, 0x1b, 0xef, 0xda, 0x4e, + 0x3b, 0xa0, 0x6d, 0xbb, 0x5d, 0xa7, 0x4f, 0x27, 0x6e, 0xe7, 0x96, 0x26, 0x96, 0x5e, 0xd2, 0xfd, + 0x57, 0x13, 0xb5, 0xf5, 0xee, 0x2d, 0xb9, 0x8c, 0x79, 0x33, 0xea, 0x7c, 0xf1, 0xe6, 0x79, 0x9c, + 0x64, 0x9b, 0x01, 0xd4, 0x38, 0x49, 0xc4, 0x30, 0xbe, 0x1f, 0x9e, 0xef, 0x5f, 0x01, 0xf9, 0x08, + 0x26, 0xf0, 0xbd, 0x9e, 0x7b, 0x9d, 0x5d, 0xcf, 0x6e, 0x50, 0x69, 0xd3, 0x92, 0x26, 0x45, 0xb5, + 0x8c, 0xe7, 0x0a, 0x11, 0x71, 0x7b, 0xbb, 0xff, 0x1f, 0x75, 0x57, 0xf7, 0xe3, 0xb6, 0x71, 0xed, + 0x4d, 0x49, 0xbb, 0xde, 0x3d, 0xda, 0x0f, 0xee, 0xc4, 0xb1, 0xf7, 0xae, 0xed, 0x8d, 0xa3, 0xeb, + 0x38, 0xb1, 0x92, 0x38, 0x71, 0x7c, 0xf3, 0xe1, 0xdc, 0x9b, 0x9b, 0x70, 0x25, 0x6a, 0xc5, 0xac, + 0x44, 0x2a, 0x24, 0xb5, 0x1b, 0x27, 0x69, 0x09, 0x65, 0x45, 0xef, 0xaa, 0xd1, 0x52, 0x8a, 0x44, + 0xc5, 0x71, 0x9e, 0xda, 0x97, 0x3c, 0xb4, 0x48, 0xd1, 0x06, 0x7d, 0x28, 0xda, 0x02, 0x45, 0x81, + 0xfc, 0x17, 0xfd, 0x07, 0x02, 0x04, 0x05, 0xf2, 0xd0, 0xb7, 0x02, 0x41, 0x6b, 0xa0, 0x2f, 0x7d, + 0x2d, 0xfa, 0x92, 0xa7, 0x62, 0xce, 0xcc, 0x90, 0x43, 0xea, 0x23, 0xbb, 0x76, 0xd2, 0xa2, 0x6f, + 0xd2, 0xcc, 0x39, 0xc3, 0x21, 0x67, 0xe6, 0xcc, 0x39, 0x73, 0xe6, 0xfc, 0x0e, 0x66, 0x02, 0xe2, + 0x4c, 0x89, 0xdb, 0xc3, 0x72, 0x6b, 0x85, 0x1f, 0x2a, 0x40, 0xc6, 0xdb, 0x20, 0x2f, 0xc0, 0x52, + 0xd3, 0x2d, 0x39, 0x61, 0x6b, 0x10, 0x56, 0x7b, 0xa3, 0x01, 0xc7, 0xe0, 0x60, 0x61, 0x5c, 0xe1, + 0x3e, 0x95, 0x0c, 0x83, 0xd0, 0x3b, 0xec, 0x8d, 0x06, 0x76, 0x82, 0x0e, 0x01, 0xed, 0x7d, 0xff, + 0xbd, 0x76, 0xeb, 0x6e, 0x12, 0xd0, 0x9e, 0x97, 0x25, 0x00, 0xed, 0x79, 0x59, 0xe1, 0x33, 0x05, + 0xce, 0x8b, 0x1b, 0x0f, 0xed, 0x09, 0x7d, 0x29, 0x61, 0xb0, 0xf2, 0x40, 0x60, 0x90, 0xcd, 0xd2, + 0x4d, 0xd7, 0x44, 0x3c, 0x3f, 0x76, 0x10, 0x95, 0x54, 0xc6, 0x4b, 0x5e, 0x83, 0x9c, 0x13, 0xf6, + 0xfa, 0xc7, 0x08, 0xe8, 0x57, 0xa3, 0x11, 0x0d, 0x7b, 0x7d, 0x6c, 0x02, 0x39, 0x0b, 0x3e, 0x9c, + 0x91, 0x3b, 0x27, 0x7a, 0x4c, 0xea, 0x70, 0x9a, 0x83, 0xb4, 0xa4, 0xbc, 0x43, 0x33, 0xde, 0x69, + 0x6b, 0x55, 0xa0, 0x06, 0x70, 0x0c, 0x2c, 0x5b, 0xb4, 0x51, 0xf8, 0xa9, 0x02, 0x79, 0xaa, 0x3c, + 0xa0, 0x39, 0xf6, 0xa0, 0x53, 0x3a, 0xa9, 0x07, 0x0a, 0xe7, 0x62, 0xd4, 0xfc, 0xb1, 0x36, 0xd7, + 0xe7, 0x61, 0x35, 0xc5, 0x40, 0x0a, 0x18, 0x2f, 0xda, 0xed, 0xec, 0xb7, 0x18, 0x3e, 0x36, 0xf3, + 0xcc, 0x25, 0xca, 0x0a, 0x3f, 0x56, 0xe0, 0x0c, 0x35, 0xde, 0x0d, 0x3c, 0xb7, 0xb5, 0x47, 0x5d, + 0xb1, 0xde, 0xa9, 0x42, 0x24, 0xae, 0xce, 0xb0, 0x58, 0x36, 0xa6, 0x10, 0xf1, 0x32, 0x3b, 0xaa, + 0x25, 0x55, 0x58, 0xe0, 0xfb, 0xcb, 0x90, 0x43, 0x57, 0x6d, 0x4a, 0xa7, 0x02, 0x71, 0xc3, 0x9c, + 0x88, 0xbe, 0x09, 0x8a, 0x30, 0xce, 0x63, 0x47, 0xdc, 0x85, 0xbf, 0x2b, 0x70, 0x6e, 0x0a, 0x0f, + 0x79, 0x05, 0xe6, 0xf0, 0x4a, 0x3e, 0x1f, 0xbd, 0x0b, 0x53, 0x1e, 0x11, 0xee, 0x1f, 0xee, 0x5e, + 0x67, 0x1b, 0xd1, 0x11, 0xfd, 0x63, 0x33, 0x2e, 0xf2, 0x36, 0x2c, 0x6a, 0xed, 0x36, 0xb7, 0x4b, + 0x32, 0x09, 0xbb, 0x64, 0xca, 0x13, 0xaf, 0x45, 0xf4, 0xcc, 0x2e, 0x61, 0x97, 0x43, 0xdb, 0x6d, + 0x8f, 0x87, 0x1b, 0xc4, 0xed, 0x6d, 0xfc, 0x1f, 0xac, 0x24, 0x89, 0x4f, 0x64, 0x97, 0xfc, 0x52, + 0x01, 0x35, 0xd9, 0x87, 0xef, 0x06, 0xef, 0x60, 0xd2, 0x30, 0x7f, 0xc3, 0xa4, 0xfa, 0x79, 0x06, + 0x1e, 0x9e, 0xf8, 0x85, 0xc9, 0xd3, 0x30, 0xaf, 0xf5, 0xfb, 0x46, 0x99, 0xcf, 0x2a, 0xae, 0xf0, + 0xe0, 0x71, 0x6f, 0xc2, 0x6c, 0x63, 0x44, 0xe4, 0x06, 0x2c, 0xe0, 0xcc, 0xa4, 0x0c, 0x99, 0x18, + 0x44, 0x8a, 0x9d, 0x86, 0xa4, 0x40, 0xa4, 0x04, 0x21, 0xa9, 0xc0, 0x0a, 0x0f, 0x7d, 0xb6, 0xfd, + 0x03, 0xff, 0xc3, 0x08, 0xcd, 0x14, 0x01, 0x57, 0xc5, 0x19, 0xb2, 0x37, 0x60, 0x75, 0x72, 0xf0, + 0x6f, 0x92, 0x0b, 0x73, 0xea, 0xd3, 0x36, 0xe5, 0x96, 0x18, 0x92, 0x15, 0xcb, 0xa9, 0x8f, 0x9d, + 0x98, 0xd2, 0xd6, 0x18, 0x67, 0x34, 0x5c, 0xda, 0x70, 0xd8, 0x39, 0x08, 0x8e, 0xfc, 0x20, 0xfc, + 0xee, 0x86, 0x2b, 0x7e, 0xc6, 0xb1, 0x86, 0xeb, 0x17, 0x39, 0xb6, 0x98, 0xd3, 0x6c, 0x54, 0xa3, + 0x91, 0xc0, 0x0b, 0x51, 0xa3, 0xc1, 0x2c, 0x92, 0x2c, 0xb8, 0xb7, 0x0c, 0xa7, 0x59, 0xd0, 0xb5, + 0x58, 0x19, 0x17, 0x27, 0x76, 0x81, 0xd1, 0xec, 0x5e, 0x67, 0xea, 0x0b, 0x8b, 0x0d, 0x18, 0xda, + 0x82, 0x95, 0xec, 0x42, 0xbe, 0xd4, 0xf5, 0x5b, 0xc1, 0xa8, 0xef, 0x1e, 0xcf, 0x0d, 0xb8, 0xce, + 0xdf, 0x65, 0x69, 0x9f, 0xb1, 0xa1, 0xfb, 0x10, 0x25, 0xb9, 0xdc, 0x10, 0x71, 0xa3, 0xeb, 0xc2, + 0x39, 0x3c, 0x72, 0x7c, 0x76, 0xc6, 0xf7, 0x49, 0x17, 0x22, 0x5f, 0xf2, 0x2e, 0x3c, 0xbf, 0x4f, + 0xec, 0xc1, 0x4a, 0xad, 0x35, 0x0c, 0xdd, 0x41, 0x2b, 0x18, 0x22, 0xe4, 0xd2, 0x31, 0xc0, 0x2c, + 0x44, 0xbe, 0x59, 0x76, 0x14, 0x19, 0x46, 0xac, 0xec, 0x28, 0x32, 0xd9, 0x1c, 0xd5, 0x97, 0x2a, + 0x9d, 0xa0, 0xd5, 0xed, 0x7c, 0x24, 0xa2, 0x2a, 0x98, 0xbe, 0x74, 0x5b, 0x14, 0xda, 0x71, 0x7d, + 0xe1, 0x9d, 0xb1, 0x71, 0x63, 0xbd, 0xcc, 0xc3, 0x69, 0x1e, 0x48, 0xc7, 0x02, 0xcb, 0x1a, 0xba, + 0x59, 0x36, 0xcc, 0x6d, 0x55, 0x21, 0x2b, 0x00, 0x0d, 0xdb, 0x2a, 0xe9, 0x8e, 0x43, 0xff, 0x67, + 0xe8, 0x7f, 0x1e, 0x75, 0x56, 0x69, 0xd6, 0xd4, 0xac, 0x14, 0x78, 0x96, 0x2b, 0x7c, 0xa1, 0xc0, + 0xd9, 0xc9, 0x43, 0x49, 0x5c, 0xc0, 0xd0, 0x43, 0xee, 0x10, 0x7e, 0x61, 0xe6, 0xb8, 0x4f, 0x2c, + 0x4e, 0x87, 0x30, 0x86, 0x2c, 0x34, 0x2e, 0x23, 0xbc, 0x3e, 0x71, 0xa6, 0xd4, 0x4e, 0xbb, 0x50, + 0x82, 0xf5, 0x69, 0x6d, 0x24, 0x5f, 0x75, 0x15, 0xf2, 0x5a, 0xa3, 0x51, 0x33, 0x4a, 0x9a, 0x6b, + 0x58, 0xa6, 0xaa, 0x90, 0x45, 0x98, 0xdb, 0xb6, 0xad, 0x66, 0x43, 0xcd, 0x14, 0x3e, 0x55, 0x60, + 0xd9, 0x08, 0x42, 0xff, 0x80, 0xdd, 0x52, 0x7d, 0xd0, 0xc5, 0xf7, 0x72, 0x62, 0xf1, 0xad, 0x47, + 0x41, 0xba, 0xd1, 0x03, 0x8e, 0xb5, 0xf2, 0xfe, 0xa8, 0xc0, 0xda, 0x18, 0x0f, 0x71, 0xe0, 0xb4, + 0xb6, 0xe7, 0x58, 0x46, 0xb9, 0xc4, 0x7b, 0x26, 0xb4, 0x72, 0x5e, 0x3a, 0xfe, 0x14, 0x16, 0x03, + 0x73, 0x67, 0xe8, 0xf5, 0x3a, 0x6d, 0x29, 0x3f, 0x53, 0xf5, 0x94, 0x2d, 0x5a, 0xc2, 0x9d, 0xec, + 0xa3, 0xd1, 0xc0, 0xc7, 0x66, 0x33, 0x89, 0x13, 0xcd, 0xa8, 0x7c, 0xbc, 0x61, 0xbc, 0x94, 0xd9, + 0xa2, 0xf5, 0xe3, 0x4d, 0xc7, 0xed, 0x6d, 0x2d, 0x43, 0x9e, 0x5b, 0x2d, 0x68, 0x10, 0x7c, 0xa2, + 0xc0, 0xfa, 0xb4, 0xbe, 0x52, 0x43, 0x28, 0x19, 0x10, 0x77, 0x36, 0x02, 0xeb, 0x4d, 0x46, 0xc2, + 0x09, 0x32, 0xf2, 0x2a, 0xe4, 0x59, 0xfa, 0x69, 0xe7, 0x46, 0xd3, 0x36, 0xf8, 0x04, 0xb9, 0xf8, + 0xb7, 0xaf, 0x1e, 0x39, 0xc7, 0x92, 0x55, 0x7b, 0xc3, 0x1b, 0xde, 0x68, 0xd0, 0x49, 0x00, 0x9b, + 0xca, 0x1c, 0x85, 0x8f, 0x15, 0xd8, 0x98, 0xfe, 0x92, 0x74, 0x97, 0x71, 0xa9, 0x6e, 0x1e, 0xc7, + 0x14, 0xe1, 0x2e, 0x83, 0xfa, 0x7a, 0x2a, 0xa8, 0x28, 0x22, 0xa4, 0x4c, 0x51, 0xe6, 0xc3, 0xcc, + 0x58, 0xc2, 0xb3, 0x24, 0x93, 0x20, 0x2c, 0xfc, 0x3a, 0x03, 0x67, 0xe9, 0x04, 0xea, 0xfa, 0xc3, + 0xa1, 0x36, 0x0a, 0x0f, 0xfd, 0x20, 0xe4, 0x2a, 0x15, 0x79, 0x11, 0xe6, 0x0f, 0x4f, 0x76, 0x1a, + 0xc8, 0xc8, 0x09, 0x01, 0x14, 0xca, 0xe2, 0x52, 0x27, 0xfd, 0x4d, 0x2e, 0x82, 0x94, 0x60, 0x0e, + 0x65, 0xea, 0x92, 0xbd, 0xd8, 0x8f, 0xd2, 0xcc, 0xbd, 0x04, 0x73, 0x68, 0xfd, 0x73, 0xd1, 0x28, + 0x54, 0xda, 0xc9, 0x3d, 0xc3, 0xb3, 0x01, 0x9b, 0x31, 0x90, 0x67, 0x00, 0x62, 0x50, 0x58, 0x2e, + 0xfb, 0x84, 0x19, 0x1d, 0xe1, 0xc2, 0xda, 0x8b, 0x47, 0xb7, 0x5b, 0x1c, 0x69, 0xb5, 0x08, 0x6b, + 0xe2, 0x93, 0xf4, 0x05, 0x76, 0x0d, 0x77, 0x4f, 0xad, 0xb2, 0x0a, 0xa3, 0xcf, 0xf1, 0x6b, 0x0a, + 0x7f, 0xcd, 0xc0, 0xe2, 0x1e, 0x55, 0x14, 0xd0, 0xfc, 0x9d, 0x6d, 0x4e, 0x3f, 0x07, 0xf9, 0x5a, + 0xaf, 0xc5, 0xcf, 0xee, 0x87, 0x1c, 0x3e, 0x0b, 0x03, 0x6b, 0xba, 0xbd, 0x96, 0x70, 0x03, 0x0c, + 0x6d, 0x99, 0xe8, 0x1b, 0x82, 0x82, 0x5e, 0x87, 0x79, 0xe6, 0x4b, 0xe1, 0x07, 0x35, 0x42, 0x55, + 0x8c, 0x7a, 0x74, 0x8d, 0x55, 0x4b, 0xc7, 0xcd, 0xcc, 0x1f, 0x23, 0xeb, 0x2d, 0x1c, 0x3d, 0x4b, + 0x32, 0xf6, 0xe7, 0x8e, 0x67, 0xec, 0x4b, 0x28, 0x21, 0xf3, 0xc7, 0x41, 0x09, 0xd9, 0xb8, 0x09, + 0x79, 0xa9, 0x3f, 0x27, 0xd2, 0x1c, 0x7f, 0x94, 0x81, 0x65, 0x7c, 0xab, 0xe8, 0x62, 0xc5, 0x7f, + 0xe6, 0xd1, 0xc5, 0xcb, 0x89, 0xa3, 0x8b, 0x75, 0x79, 0xbc, 0xd8, 0x9b, 0xcd, 0x38, 0xb3, 0x78, + 0x1d, 0xd6, 0xc6, 0x08, 0xc9, 0xf3, 0x30, 0x47, 0xbb, 0x2f, 0x4c, 0x3d, 0x35, 0x3d, 0x03, 0x62, + 0x44, 0x39, 0xfa, 0xe2, 0x43, 0x9b, 0x51, 0x17, 0xfe, 0xa1, 0xc0, 0x12, 0x47, 0x09, 0x0e, 0x6e, + 0xf7, 0xbe, 0xf1, 0x73, 0x5e, 0x49, 0x7f, 0x4e, 0x16, 0xe2, 0xca, 0x3f, 0xe7, 0xbf, 0xfa, 0x23, + 0xde, 0x4c, 0x7c, 0xc4, 0x73, 0x11, 0xbe, 0x8c, 0x78, 0x9d, 0x19, 0xdf, 0xf0, 0xf7, 0x88, 0xb8, + 0x96, 0x24, 0x24, 0xdf, 0x87, 0x45, 0xd3, 0xbf, 0x93, 0xb0, 0x98, 0xae, 0x4c, 0x69, 0xf4, 0x5a, + 0x44, 0xc8, 0xd6, 0x14, 0x6e, 0x36, 0x81, 0x7f, 0xc7, 0x1b, 0x73, 0xe3, 0xc4, 0x4d, 0x52, 0xa3, + 0x29, 0xc9, 0x76, 0x92, 0xa9, 0xcf, 0x03, 0x29, 0x30, 0x6a, 0xfb, 0x77, 0x39, 0x80, 0xf8, 0x0e, + 0x3a, 0x5d, 0x80, 0x09, 0x0f, 0xb6, 0x38, 0x3b, 0xc6, 0x22, 0x79, 0x8e, 0x0b, 0xc7, 0xf6, 0x15, + 0x7e, 0x28, 0x9a, 0x99, 0x8e, 0xff, 0x83, 0xc7, 0xa3, 0x25, 0x7e, 0xc7, 0xbb, 0xed, 0x77, 0x5b, + 0x4c, 0x16, 0x67, 0xb7, 0x2e, 0x23, 0xdc, 0x5b, 0x54, 0x3a, 0x25, 0xdd, 0x1b, 0xde, 0x04, 0x2f, + 0x53, 0x82, 0xb1, 0xb8, 0x8e, 0xdc, 0xfd, 0xc7, 0x75, 0xcc, 0xdd, 0x47, 0x5c, 0xc7, 0xfc, 0x31, + 0xe3, 0x3a, 0x1a, 0xb0, 0xd8, 0x09, 0x3e, 0xf0, 0x83, 0xb0, 0x37, 0xb8, 0x8b, 0x5e, 0xea, 0xf8, + 0x28, 0x8b, 0x7e, 0x6a, 0x43, 0xd4, 0xb1, 0xf1, 0xc6, 0x0d, 0x33, 0xa2, 0x97, 0x87, 0x3b, 0x2a, + 0x24, 0xff, 0x03, 0xb1, 0xd7, 0x83, 0x43, 0x7b, 0x4f, 0xdf, 0x67, 0xf7, 0x85, 0x53, 0xe4, 0x35, + 0x48, 0x3a, 0x3f, 0x78, 0x54, 0x24, 0xcb, 0x27, 0x2a, 0x57, 0xc8, 0x90, 0x57, 0xfb, 0x92, 0x7f, + 0x84, 0x5f, 0x7b, 0xfd, 0x3a, 0x03, 0x64, 0xbc, 0xe3, 0xe4, 0x65, 0xc8, 0x33, 0xd1, 0xef, 0x0d, + 0x86, 0xef, 0xf3, 0x60, 0x04, 0x16, 0x95, 0x2f, 0x15, 0xcb, 0x51, 0xf9, 0xac, 0xd8, 0x1e, 0xbe, + 0xdf, 0x25, 0xdf, 0x83, 0x87, 0x70, 0xe0, 0xfb, 0xfe, 0xa0, 0xd3, 0x6b, 0x7b, 0x88, 0x8b, 0xd6, + 0xea, 0xf2, 0xa4, 0x31, 0x4f, 0x63, 0x76, 0xb3, 0xf1, 0xea, 0x29, 0x13, 0x04, 0xef, 0xfc, 0x37, + 0x90, 0xb2, 0xc1, 0x08, 0x89, 0x0b, 0xaa, 0xcc, 0x7f, 0x7b, 0xd4, 0xed, 0xf2, 0x39, 0x57, 0xa4, + 0xe6, 0x6f, 0xba, 0x6e, 0x4a, 0xc3, 0x2b, 0x71, 0xc3, 0x95, 0x51, 0xb7, 0x4b, 0x5e, 0x04, 0xe8, + 0x05, 0xde, 0x51, 0x67, 0x38, 0x64, 0x8e, 0x8c, 0x28, 0x5e, 0x27, 0x2e, 0x95, 0x87, 0xaf, 0x17, + 0xd4, 0x59, 0x21, 0x1d, 0xbe, 0x7e, 0xeb, 0xc0, 0xc7, 0x28, 0x57, 0x76, 0x69, 0x85, 0xc3, 0x40, + 0x8b, 0xc2, 0xe4, 0x34, 0x3a, 0xf0, 0x9d, 0xce, 0x47, 0xe2, 0xce, 0xf1, 0x5b, 0xb0, 0xc6, 0xaf, + 0x8b, 0xee, 0x75, 0xc2, 0x43, 0xae, 0x77, 0x3f, 0x88, 0xd2, 0x2e, 0x29, 0xde, 0x7f, 0xca, 0x01, + 0x68, 0x7b, 0x8e, 0x00, 0x90, 0xb8, 0x0a, 0x73, 0xd4, 0x9a, 0x10, 0xa7, 0x12, 0x78, 0xa6, 0x8b, + 0xed, 0xca, 0x67, 0xba, 0x48, 0x41, 0xe5, 0x84, 0xed, 0x1f, 0xe0, 0xc1, 0x58, 0x26, 0x3e, 0xc2, + 0x18, 0xb0, 0xa2, 0x84, 0xf6, 0xca, 0x8a, 0x48, 0x0d, 0x20, 0x86, 0x74, 0xe0, 0xf6, 0xed, 0x5a, + 0x1c, 0x1b, 0xcd, 0x2b, 0x38, 0xbe, 0x6f, 0x0c, 0x0b, 0x21, 0x4f, 0x9f, 0x98, 0x8c, 0xec, 0x40, + 0xce, 0x6d, 0x45, 0xd1, 0x28, 0x53, 0x80, 0x2e, 0x2e, 0xf1, 0xa4, 0x3e, 0x31, 0xd8, 0xc5, 0x4a, + 0xd8, 0x4a, 0xe4, 0x3e, 0xc3, 0x46, 0x88, 0x0e, 0xf3, 0x3c, 0x61, 0xe3, 0x14, 0xd4, 0x23, 0x9e, + 0xaf, 0x91, 0x63, 0x1d, 0x62, 0xa1, 0xac, 0xed, 0xf0, 0xd4, 0x8c, 0xcf, 0x41, 0xd6, 0x71, 0xea, + 0x3c, 0xbc, 0x73, 0x39, 0xb6, 0x55, 0x1c, 0xa7, 0x2e, 0x92, 0xd2, 0x1e, 0x49, 0x6c, 0x94, 0x98, + 0xfc, 0x2f, 0xe4, 0x25, 0x45, 0x9c, 0x07, 0x46, 0xe3, 0x37, 0xe8, 0xc4, 0xc5, 0xb2, 0x38, 0x93, + 0xa8, 0x49, 0x0d, 0xd4, 0x9d, 0xd1, 0xbb, 0xbe, 0xd6, 0xef, 0x63, 0x24, 0xc5, 0x07, 0xfe, 0x80, + 0x61, 0x0f, 0x2f, 0xc4, 0x30, 0x81, 0x5e, 0xab, 0xdf, 0xf7, 0xda, 0xa2, 0x56, 0x3e, 0x99, 0x49, + 0x73, 0x92, 0x06, 0xac, 0x39, 0x7e, 0x38, 0xea, 0xb3, 0x6b, 0x18, 0x95, 0xde, 0x80, 0x9a, 0x26, + 0x4c, 0x60, 0x20, 0xa2, 0xda, 0x90, 0x56, 0x8a, 0xbb, 0x2f, 0xb7, 0x7b, 0x83, 0x94, 0x99, 0x32, + 0xce, 0x5c, 0xf0, 0xe5, 0x21, 0xa7, 0xfb, 0x7d, 0xd2, 0xe0, 0xc1, 0xfd, 0x5e, 0x18, 0x3c, 0xb1, + 0x99, 0xf3, 0xcc, 0x04, 0xa8, 0x0f, 0x74, 0xa3, 0x49, 0x50, 0x1f, 0x09, 0x80, 0x8f, 0xcf, 0x72, + 0x12, 0x84, 0x14, 0x1f, 0x8b, 0x57, 0x00, 0x5e, 0xef, 0x75, 0x82, 0xba, 0x1f, 0x1e, 0xf6, 0xda, + 0x12, 0xe2, 0x48, 0xfe, 0x07, 0xbd, 0x4e, 0xe0, 0x1d, 0x61, 0xf1, 0xd7, 0x5f, 0x3d, 0x22, 0x11, + 0xd9, 0xd2, 0x6f, 0xf2, 0x14, 0x2c, 0xd2, 0x7f, 0x6e, 0x7c, 0x99, 0x84, 0x1d, 0x60, 0x22, 0x37, + 0x4f, 0x6e, 0x1d, 0x11, 0x90, 0x9b, 0x08, 0x10, 0xde, 0xe9, 0x87, 0x92, 0x5a, 0x2d, 0xd0, 0xc0, + 0x3b, 0xfd, 0x30, 0x8d, 0x0a, 0x28, 0x11, 0x93, 0x6a, 0xd4, 0x75, 0x01, 0x31, 0xcf, 0x71, 0xc8, + 0xf1, 0x94, 0x8e, 0xcf, 0x35, 0x4f, 0xc0, 0x91, 0xc9, 0xc9, 0xc0, 0x52, 0x6c, 0xd8, 0x09, 0xa7, + 0x5a, 0x66, 0x6e, 0x15, 0xbe, 0xbb, 0xb1, 0x4e, 0x0c, 0x0f, 0xdb, 0xde, 0x3e, 0x16, 0x27, 0x3a, + 0x11, 0x11, 0x93, 0x2d, 0x58, 0x65, 0x71, 0xf1, 0x51, 0xaa, 0x1a, 0xbe, 0xd3, 0xa1, 0x6c, 0x8b, + 0x73, 0xd9, 0xc8, 0x8f, 0x4f, 0x31, 0x90, 0x0a, 0xcc, 0xa1, 0x69, 0xc9, 0x2f, 0x83, 0x9f, 0x97, + 0x6d, 0xea, 0xf4, 0x3a, 0x42, 0xb9, 0x82, 0xd6, 0xb4, 0x2c, 0x57, 0x90, 0x94, 0xbc, 0x09, 0xa0, + 0x07, 0x83, 0x5e, 0xb7, 0x8b, 0x80, 0x79, 0x0b, 0x68, 0x98, 0x5d, 0x4c, 0xae, 0x47, 0x6c, 0x25, + 0x26, 0xe2, 0x38, 0x30, 0xf8, 0xdf, 0x4b, 0xc1, 0xea, 0x49, 0x6d, 0x15, 0x0c, 0x98, 0x67, 0x8b, + 0x11, 0xc1, 0x27, 0x39, 0x28, 0xb6, 0x04, 0x5d, 0xc8, 0xc0, 0x27, 0x79, 0xf9, 0x38, 0xf8, 0xa4, + 0xc4, 0x50, 0xd8, 0x81, 0x33, 0x93, 0x5e, 0x2c, 0x61, 0x0c, 0x2b, 0xc7, 0x35, 0x86, 0x7f, 0x9b, + 0x85, 0x25, 0x6c, 0x4d, 0x48, 0x61, 0x0d, 0x96, 0x9d, 0xd1, 0xbb, 0x11, 0x88, 0x83, 0x90, 0xc6, + 0xd8, 0xbf, 0xa1, 0x5c, 0x21, 0x3b, 0xbc, 0x12, 0x1c, 0x44, 0x87, 0x15, 0xb1, 0x13, 0x6c, 0x8b, + 0x0b, 0xe6, 0x11, 0xee, 0xa3, 0x80, 0x17, 0x1a, 0x4f, 0xd5, 0x95, 0x62, 0x8a, 0xf7, 0x83, 0xec, + 0x49, 0xf6, 0x83, 0xdc, 0xb1, 0xf6, 0x83, 0xb7, 0x61, 0x49, 0x3c, 0x0d, 0x25, 0xf9, 0xdc, 0x83, + 0x49, 0xf2, 0x44, 0x63, 0xa4, 0x16, 0x49, 0xf4, 0xf9, 0x99, 0x12, 0x1d, 0xbd, 0x88, 0x62, 0x95, + 0x8d, 0x65, 0xdf, 0xe5, 0x6d, 0x60, 0x2e, 0x9b, 0xed, 0x52, 0xe3, 0x3e, 0x76, 0xc9, 0xe7, 0x61, + 0xb1, 0xd6, 0x13, 0x0e, 0x24, 0xe9, 0xe4, 0xbe, 0x2b, 0x0a, 0x65, 0x75, 0x21, 0xa2, 0x8c, 0x76, + 0xb7, 0xec, 0xb7, 0xb1, 0xbb, 0xdd, 0x04, 0xe0, 0x91, 0x0b, 0x71, 0x0e, 0x0a, 0x5c, 0x32, 0x22, + 0x46, 0x37, 0xe9, 0x40, 0x90, 0x88, 0xa9, 0x74, 0xe2, 0x57, 0x4d, 0xb4, 0xfd, 0xfd, 0xde, 0x28, + 0x08, 0x13, 0x49, 0xdb, 0x78, 0xb8, 0x3e, 0xdd, 0x12, 0xb0, 0x4e, 0x16, 0x0f, 0x29, 0xb6, 0x6f, + 0x77, 0x40, 0xc8, 0x1b, 0xd1, 0x1d, 0xb9, 0x99, 0x39, 0xac, 0x0b, 0x63, 0x5f, 0x68, 0xea, 0xcd, + 0xb8, 0xc2, 0x17, 0x8a, 0x0c, 0xba, 0x7b, 0x1f, 0x43, 0xfd, 0x12, 0x40, 0xe4, 0xc1, 0x17, 0x63, + 0xcd, 0x2c, 0xb9, 0xa8, 0x54, 0xfe, 0xca, 0x31, 0xad, 0xf4, 0x36, 0xd9, 0x6f, 0xeb, 0x6d, 0x5c, + 0xc8, 0x5b, 0xef, 0x85, 0xad, 0xf8, 0xca, 0x07, 0x38, 0x91, 0x26, 0x8b, 0x92, 0x49, 0xe4, 0xda, + 0x8e, 0xf5, 0xe0, 0xa9, 0xb9, 0xb6, 0x23, 0xc6, 0xc2, 0x1b, 0xb0, 0x2a, 0x07, 0x12, 0xde, 0x0d, + 0xf6, 0xc9, 0xff, 0x33, 0xb4, 0x30, 0x25, 0x61, 0xe3, 0x48, 0x44, 0x54, 0xe2, 0xde, 0x0d, 0xf6, + 0x99, 0xfe, 0xd3, 0xba, 0x23, 0xf7, 0x15, 0xad, 0xcf, 0x2f, 0x15, 0x20, 0xe3, 0xe4, 0xb2, 0x34, + 0x51, 0xfe, 0x0d, 0xda, 0x65, 0x4a, 0x2b, 0xcb, 0x9d, 0x44, 0x2b, 0x2b, 0xfe, 0x4c, 0x81, 0x55, + 0x43, 0xab, 0x73, 0x84, 0x5c, 0xe6, 0x89, 0x78, 0x14, 0x2e, 0x1a, 0x5a, 0xdd, 0x6b, 0x58, 0x35, + 0xa3, 0x74, 0xcb, 0x9b, 0x08, 0x7c, 0x77, 0x11, 0xfe, 0x6b, 0x9c, 0x24, 0xf6, 0x58, 0x5c, 0x80, + 0xf5, 0xf1, 0x6a, 0x01, 0x8e, 0x37, 0x99, 0x59, 0xe0, 0xe8, 0x65, 0x8b, 0xaf, 0xc2, 0xaa, 0xc0, + 0x8c, 0x73, 0x6b, 0x0e, 0x42, 0xcd, 0xae, 0x42, 0x7e, 0x57, 0xb7, 0x8d, 0xca, 0x2d, 0xaf, 0xd2, + 0xac, 0xd5, 0xd4, 0x53, 0x64, 0x19, 0x16, 0x79, 0x41, 0x49, 0x53, 0x15, 0xb2, 0x04, 0x0b, 0x86, + 0xe9, 0xe8, 0xa5, 0xa6, 0xad, 0xab, 0x99, 0xe2, 0xab, 0xb0, 0xd2, 0x18, 0x74, 0x3e, 0x68, 0x85, + 0xfe, 0x8e, 0x7f, 0x17, 0x1d, 0x0e, 0xa7, 0x21, 0x6b, 0x6b, 0x7b, 0xea, 0x29, 0x02, 0x30, 0xdf, + 0xd8, 0x29, 0x39, 0xd7, 0xaf, 0xab, 0x0a, 0xc9, 0xc3, 0xe9, 0xed, 0x52, 0xc3, 0xdb, 0xa9, 0x3b, + 0x6a, 0x86, 0xfe, 0xd1, 0xf6, 0x1c, 0xfc, 0x93, 0x2d, 0x3e, 0x0b, 0x6b, 0xa8, 0x2b, 0xd4, 0x3a, + 0xc3, 0xd0, 0x0f, 0xfc, 0x01, 0xf6, 0x61, 0x09, 0x16, 0x1c, 0x9f, 0x2e, 0xf2, 0xd0, 0x67, 0x1d, + 0xa8, 0x8f, 0xba, 0x61, 0xa7, 0xdf, 0xf5, 0x3f, 0x54, 0x95, 0xe2, 0x4d, 0x58, 0xb5, 0x7b, 0xa3, + 0xb0, 0x13, 0x1c, 0x38, 0x21, 0xa5, 0x38, 0xb8, 0x4b, 0x1e, 0x86, 0xb5, 0xa6, 0xa9, 0xd5, 0xb7, + 0x8c, 0xed, 0xa6, 0xd5, 0x74, 0xbc, 0xba, 0xe6, 0x96, 0xaa, 0xcc, 0xdd, 0x51, 0xb7, 0x1c, 0xd7, + 0xb3, 0xf5, 0x92, 0x6e, 0xba, 0xaa, 0x52, 0xfc, 0x89, 0x02, 0x2b, 0xcd, 0x21, 0xbf, 0xa2, 0xdb, + 0xc4, 0x40, 0xbb, 0x4b, 0x70, 0xa1, 0xe9, 0xe8, 0xb6, 0xe7, 0x5a, 0x3b, 0xba, 0xe9, 0x35, 0x1d, + 0x6d, 0x3b, 0x8d, 0xba, 0xf8, 0x08, 0x9c, 0x97, 0x28, 0x6c, 0xbd, 0x64, 0xed, 0xea, 0xb6, 0xd7, + 0xd0, 0x1c, 0x67, 0xcf, 0xb2, 0xcb, 0xaa, 0x42, 0x36, 0xe0, 0xec, 0x04, 0x82, 0x7a, 0x45, 0x53, + 0x33, 0x63, 0x75, 0xa6, 0xbe, 0xa7, 0xd5, 0xbc, 0x2d, 0xcb, 0x55, 0xb3, 0xc5, 0x3a, 0xdd, 0xe8, + 0x10, 0x98, 0x8c, 0xc1, 0xca, 0x2f, 0x40, 0xce, 0xb4, 0x4c, 0x3d, 0xed, 0x92, 0x5a, 0x82, 0x05, + 0xad, 0xd1, 0xb0, 0xad, 0x5d, 0x1c, 0x50, 0x80, 0xf9, 0xb2, 0x6e, 0xd2, 0x9e, 0x65, 0x69, 0x4d, + 0xc3, 0xb6, 0xea, 0x96, 0xab, 0x97, 0xd5, 0x5c, 0xd1, 0x16, 0x0b, 0x46, 0x34, 0xba, 0xdf, 0x63, + 0xfe, 0x9f, 0xb2, 0x5e, 0xd1, 0x9a, 0x35, 0x97, 0x7f, 0x90, 0x5b, 0x9e, 0xad, 0xbf, 0xd1, 0xd4, + 0x1d, 0xd7, 0x51, 0x15, 0xa2, 0xc2, 0x92, 0xa9, 0xeb, 0x65, 0xc7, 0xb3, 0xf5, 0x5d, 0x43, 0xdf, + 0x53, 0x33, 0xb4, 0x4d, 0xf6, 0x9b, 0x3e, 0xa1, 0xf8, 0x99, 0x02, 0x84, 0x81, 0xba, 0x09, 0xf8, + 0x6f, 0x1c, 0x9f, 0x4d, 0xd8, 0xa8, 0xd2, 0x0f, 0x8b, 0xaf, 0x56, 0xb7, 0xca, 0xe9, 0x4f, 0x76, + 0x16, 0x48, 0xaa, 0xde, 0xaa, 0x54, 0x54, 0x85, 0x9c, 0x87, 0x87, 0x52, 0xe5, 0x65, 0xdb, 0x6a, + 0xa8, 0x99, 0x8d, 0xcc, 0x82, 0x42, 0xce, 0x8d, 0x55, 0xee, 0xe8, 0x7a, 0x43, 0xcd, 0xd2, 0x21, + 0x4a, 0x55, 0x88, 0x09, 0xc8, 0xd8, 0x73, 0xc5, 0x8f, 0x15, 0x38, 0xcb, 0xba, 0x29, 0x66, 0x73, + 0xd4, 0xd5, 0x0b, 0xb0, 0xce, 0xf1, 0x27, 0x27, 0x75, 0xf4, 0x0c, 0xa8, 0x89, 0x5a, 0xd6, 0xcd, + 0x87, 0x61, 0x2d, 0x51, 0x8a, 0xfd, 0xc8, 0xd0, 0xb5, 0x9a, 0x28, 0xde, 0xd2, 0x1d, 0xd7, 0xd3, + 0x2b, 0x15, 0xcb, 0x76, 0x59, 0x47, 0xb2, 0xc5, 0x02, 0xac, 0x95, 0xfc, 0x41, 0x48, 0x6d, 0x90, + 0x60, 0xd8, 0xe9, 0x05, 0xd8, 0x85, 0x65, 0x58, 0xd4, 0xdf, 0x74, 0x75, 0xd3, 0x31, 0x2c, 0x53, + 0x3d, 0x55, 0xbc, 0x90, 0xa2, 0x11, 0xab, 0xc6, 0x71, 0xaa, 0xea, 0xa9, 0x62, 0x0b, 0x96, 0xc5, + 0x95, 0x58, 0x36, 0x2b, 0x36, 0x61, 0x43, 0xcc, 0x35, 0x5c, 0xbf, 0xe9, 0x57, 0x58, 0x87, 0x33, + 0xe3, 0xf5, 0xba, 0xab, 0x2a, 0x74, 0x14, 0x52, 0x35, 0xb4, 0x3c, 0x53, 0xfc, 0x8d, 0x02, 0xeb, + 0x3c, 0x5d, 0x26, 0xf7, 0x47, 0x30, 0xc8, 0x6b, 0x84, 0x8f, 0x2b, 0xc2, 0x15, 0xd7, 0x6e, 0x3a, + 0xae, 0x5e, 0xf6, 0xca, 0xfa, 0xae, 0x51, 0xd2, 0x71, 0xba, 0x18, 0xb6, 0x5e, 0xd7, 0x4d, 0x37, + 0xf5, 0xe8, 0x27, 0xe1, 0xf1, 0x19, 0xb4, 0xa6, 0xe5, 0x8a, 0xff, 0x74, 0x95, 0x3c, 0x0e, 0xff, + 0x3d, 0x83, 0x38, 0x22, 0xcc, 0x14, 0xdf, 0x81, 0xa5, 0x44, 0x1a, 0x8f, 0x73, 0xf0, 0x90, 0xfc, + 0xbf, 0xe1, 0x07, 0xed, 0x4e, 0x70, 0xa0, 0x9e, 0x4a, 0x57, 0xd8, 0xa3, 0x20, 0xa0, 0x15, 0xb8, + 0x20, 0xe5, 0x0a, 0xd7, 0x1f, 0x1c, 0x75, 0x82, 0x56, 0xe8, 0xb7, 0xd5, 0x4c, 0xf1, 0x1a, 0x2c, + 0x27, 0x70, 0x06, 0xe9, 0x97, 0xaf, 0x59, 0x5c, 0x5e, 0xd5, 0xf5, 0xb2, 0xd1, 0xac, 0xab, 0x73, + 0x74, 0x29, 0x56, 0x8d, 0xed, 0xaa, 0x0a, 0xc5, 0x4f, 0x15, 0xaa, 0x31, 0xe3, 0xf7, 0xa9, 0x57, + 0x34, 0x31, 0x56, 0x74, 0x9e, 0x30, 0x48, 0x52, 0xdd, 0x71, 0x98, 0x2b, 0xf5, 0x02, 0xac, 0xf3, + 0x3f, 0x9e, 0x66, 0x96, 0xbd, 0xaa, 0x66, 0x97, 0xf7, 0x34, 0x9b, 0x4e, 0x9e, 0x5b, 0x6a, 0x06, + 0x57, 0x84, 0x54, 0xe2, 0xb9, 0x56, 0xb3, 0x54, 0x55, 0xb3, 0x74, 0x02, 0x26, 0xca, 0x1b, 0x86, + 0xa9, 0xe6, 0x70, 0x7d, 0x8d, 0x51, 0x63, 0xb3, 0xb4, 0x7e, 0xae, 0x78, 0x4f, 0x81, 0x73, 0x4e, + 0xe7, 0x20, 0x68, 0x85, 0xa3, 0x81, 0xaf, 0x75, 0x0f, 0x7a, 0x83, 0x4e, 0x78, 0x78, 0xe4, 0x8c, + 0x3a, 0xa1, 0x4f, 0xae, 0xc2, 0x63, 0x8e, 0xb1, 0x6d, 0x6a, 0x2e, 0x5d, 0x1f, 0x5a, 0x6d, 0xdb, + 0xb2, 0x0d, 0xb7, 0x5a, 0xf7, 0x9c, 0xa6, 0x31, 0x36, 0x75, 0x2e, 0xc3, 0xa5, 0xe9, 0xa4, 0x35, + 0x7d, 0x5b, 0x2b, 0xdd, 0x52, 0x95, 0xd9, 0x0d, 0x6e, 0x69, 0x35, 0xcd, 0x2c, 0xe9, 0x65, 0x6f, + 0xf7, 0xba, 0x9a, 0x21, 0x8f, 0xc1, 0xa3, 0xd3, 0x49, 0x2b, 0x46, 0xc3, 0xa1, 0x64, 0xd9, 0xd9, + 0xcf, 0xad, 0x3a, 0x75, 0x4a, 0x95, 0x2b, 0x76, 0x40, 0x4d, 0x47, 0x50, 0x8f, 0x39, 0xee, 0xed, + 0xa6, 0x69, 0x32, 0x29, 0xb9, 0x0a, 0x79, 0xcb, 0xad, 0xea, 0x36, 0x47, 0xc6, 0x45, 0x28, 0xdc, + 0xa6, 0xa9, 0x35, 0xdd, 0xaa, 0x65, 0x1b, 0x6f, 0xa1, 0xb8, 0x5c, 0x87, 0x33, 0x4e, 0x4d, 0x2b, + 0xed, 0xe0, 0xcc, 0x34, 0x4c, 0xaf, 0x54, 0xd5, 0x4c, 0x53, 0xaf, 0xa9, 0x50, 0xfc, 0x95, 0xc2, + 0x3c, 0xe8, 0x93, 0x42, 0xad, 0xc8, 0x53, 0xf0, 0x84, 0xb5, 0xe3, 0x6a, 0x5e, 0xa3, 0xd6, 0xdc, + 0x36, 0x4c, 0xcf, 0xb9, 0x65, 0x96, 0xc4, 0x46, 0x5a, 0x1a, 0x97, 0x28, 0x4f, 0xc0, 0xe5, 0x99, + 0xd4, 0x31, 0x86, 0xed, 0x15, 0x28, 0xcc, 0xa4, 0xe4, 0x2f, 0x52, 0xfc, 0x83, 0x02, 0xe7, 0x67, + 0x78, 0x1e, 0xc9, 0xd3, 0x70, 0xb5, 0xaa, 0x6b, 0xe5, 0x9a, 0xee, 0x38, 0x1e, 0x7d, 0x5f, 0xdd, + 0x74, 0xb9, 0x83, 0x7f, 0xa2, 0xbc, 0xb8, 0x0a, 0x8f, 0xcd, 0x26, 0x8f, 0x77, 0x9e, 0x27, 0xe0, + 0xf2, 0x6c, 0x52, 0xbe, 0x13, 0x65, 0xa8, 0xd4, 0x98, 0x4d, 0x19, 0xed, 0x60, 0xd9, 0xe2, 0x27, + 0x0a, 0x9c, 0x9d, 0x6c, 0xb0, 0xd3, 0xbe, 0x19, 0xa6, 0xe3, 0x6a, 0xb5, 0x9a, 0xd7, 0xd0, 0x6c, + 0xad, 0xee, 0xe9, 0xa6, 0x6d, 0xd5, 0x6a, 0x93, 0x24, 0xf7, 0x65, 0xb8, 0x34, 0x9d, 0xd4, 0x29, + 0xd9, 0x46, 0x83, 0x8a, 0xc0, 0x02, 0x6c, 0x4e, 0xa7, 0xd2, 0x8d, 0x92, 0xae, 0x66, 0xb6, 0x5e, + 0xf9, 0xfc, 0x2f, 0x9b, 0xa7, 0x3e, 0xbf, 0xb7, 0xa9, 0x7c, 0x79, 0x6f, 0x53, 0xf9, 0xf3, 0xbd, + 0x4d, 0xe5, 0xad, 0x27, 0x4f, 0x90, 0xeb, 0xfb, 0xdd, 0x79, 0xbc, 0xd1, 0x72, 0xe3, 0x9f, 0x01, + 0x00, 0x00, 0xff, 0xff, 0x73, 0x71, 0x96, 0xc4, 0xb6, 0x90, 0x01, 0x00, } func (this *PluginSpecV1) Equal(that interface{}) bool { @@ -35738,6 +35749,15 @@ func (m *OIDCAuthRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.ClientUserAgent) > 0 { + i -= len(m.ClientUserAgent) + copy(dAtA[i:], m.ClientUserAgent) + i = encodeVarintTypes(dAtA, i, uint64(len(m.ClientUserAgent))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x9a + } if len(m.ClientLoginIP) > 0 { i -= len(m.ClientLoginIP) copy(dAtA[i:], m.ClientLoginIP) @@ -36189,6 +36209,15 @@ func (m *SAMLAuthRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.ClientUserAgent) > 0 { + i -= len(m.ClientUserAgent) + copy(dAtA[i:], m.ClientUserAgent) + i = encodeVarintTypes(dAtA, i, uint64(len(m.ClientUserAgent))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x92 + } if len(m.ClientLoginIP) > 0 { i -= len(m.ClientLoginIP) copy(dAtA[i:], m.ClientLoginIP) @@ -51176,6 +51205,10 @@ func (m *OIDCAuthRequest) Size() (n int) { if l > 0 { n += 2 + l + sovTypes(uint64(l)) } + l = len(m.ClientUserAgent) + if l > 0 { + n += 2 + l + sovTypes(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -51375,6 +51408,10 @@ func (m *SAMLAuthRequest) Size() (n int) { if l > 0 { n += 2 + l + sovTypes(uint64(l)) } + l = len(m.ClientUserAgent) + if l > 0 { + n += 2 + l + sovTypes(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -95367,6 +95404,38 @@ func (m *OIDCAuthRequest) Unmarshal(dAtA []byte) error { } m.ClientLoginIP = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 19: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientUserAgent", 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.ClientUserAgent = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -96786,6 +96855,38 @@ func (m *SAMLAuthRequest) Unmarshal(dAtA []byte) error { } m.ClientLoginIP = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 18: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientUserAgent", 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.ClientUserAgent = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/build.assets/macos/tshdev/README.md b/build.assets/macos/tshdev/README.md index 67c2060ba4d20..c2b82452c3726 100644 --- a/build.assets/macos/tshdev/README.md +++ b/build.assets/macos/tshdev/README.md @@ -181,6 +181,15 @@ launch the daemon with the following error: After resetting the db and restarting the device, everything seemed to be working again. +In theory, it's possible to list all app bundles with a certain bundle identifier by running the +following command: + +``` +mdfind kMDItemCFBundleIdentifier = "com.goteleport.tshdev" +``` + +In practice, getting rid of all but one bundle didn't appear to solve the problem. + ### Daemon does not start List all jobs loaded into launchd. The second column is the status which you can then inspect. diff --git a/build.assets/versions.mk b/build.assets/versions.mk index dc050e82d6275..97ed484afcb20 100644 --- a/build.assets/versions.mk +++ b/build.assets/versions.mk @@ -17,7 +17,7 @@ LIBPCSCLITE_VERSION ?= 1.9.9-teleport DEVTOOLSET ?= devtoolset-12 # Protogen related versions. -BUF_VERSION ?= v1.35.0 +BUF_VERSION ?= v1.35.1 # Keep in sync with api/proto/buf.yaml (and buf.lock). GOGO_PROTO_TAG ?= v1.3.2 NODE_GRPC_TOOLS_VERSION ?= 1.12.4 diff --git a/docs/config.json b/docs/config.json index 176c058788d49..616e5c93d019a 100644 --- a/docs/config.json +++ b/docs/config.json @@ -1292,8 +1292,8 @@ "aws_secret_access_key": "zyxw9876-this-is-an-example" }, "cloud": { - "version": "15.4.9", - "major_version": "15", + "version": "16.1.0", + "major_version": "16", "sla": { "monthly_percentage": "99.9%", "monthly_downtime": "44 minutes" diff --git a/docs/cspell.json b/docs/cspell.json index df460af608595..ebdcca57e197f 100644 --- a/docs/cspell.json +++ b/docs/cspell.json @@ -116,6 +116,7 @@ "Kanban", "Keycloak", "Keyspaces", + "knownhosts", "Kubernetes", "Kubes", "LDAPS", @@ -183,6 +184,7 @@ "Relogin", "SAMLIDP", "SCIM", + "sendall", "SECURITYADMIN", "SIEM", "SIGINT", @@ -470,6 +472,7 @@ "fluxcd", "ftmg", "fullchain", + "fprint", "gacc", "gcloud", "gcpproj", @@ -703,6 +706,7 @@ "parquetlog", "pastable", "pasteable", + "paramiko", "persistentvolume", "persistentvolumeclaim", "pgaadauth", @@ -802,6 +806,7 @@ "serviceacct", "servicecfg", "serviceid", + "sess", "session", "setspn", "sharded", @@ -941,6 +946,7 @@ "winadj", "windowsaccountname", "windowsdesktop", + "winpty", "winscp", "winserver", "workgroups", diff --git a/docs/pages/access-controls/guides/hardware-key-support.mdx b/docs/pages/access-controls/guides/hardware-key-support.mdx index e076c59d95736..4aefa5d2a096a 100644 --- a/docs/pages/access-controls/guides/hardware-key-support.mdx +++ b/docs/pages/access-controls/guides/hardware-key-support.mdx @@ -13,15 +13,15 @@ By default, `tsh`, Teleport Connect, and other Teleport clients store a user's k on their filesystem. If a user's filesystem is compromised, any of their active Teleport user keys and certificates would also be compromised. -You can configure [per-session MFA](per-session-mfa.mdx) to require a multi-factor authentication check -when users start new sessions with Teleport services, such as the SSH Service, Kubernetes Service, Database Service, and so on. -However, per-session MFA doesn't prevent compromised session credentials from taking other actions, such as -running administrative commands with `tctl`. - -To prevent these types of attacks, Teleport supports hardware-based private keys. -Unlike disk-based private keys, hardware-based private keys are generated and stored directly -on a hardware device and are impossible to export. -With hardware-based private keys, a login session is only functional if there's also access to the +You can configure [per-session MFA](per-session-mfa.mdx) to require a multi-factor authentication check +when users start new sessions with Teleport services, such as the SSH Service, Kubernetes Service, Database Service, and so on. +However, per-session MFA doesn't prevent compromised session credentials from taking other actions, such as +running administrative commands with `tctl`. + +To prevent these types of attacks, Teleport supports hardware-based private keys. +Unlike disk-based private keys, hardware-based private keys are generated and stored directly +on a hardware device and are impossible to export. +With hardware-based private keys, a login session is only functional if there's also access to the hardware device where the key was generated and stored. Additionally, you can configure this feature to require a touch for every Teleport request, including non-session requests @@ -33,9 +33,9 @@ like `tctl edit`. With touch required, hardware key support provides better secu Hardware key support provides the best security available. However, not all services are compatible with hardware keys. - + Supported: - + - Teleport clients `tsh`, `tctl`, and Teleport Connect. - Standard Teleport API requests such as `tsh ls`, `tctl create`, and so on. - Server access. @@ -46,7 +46,7 @@ like `tctl edit`. With touch required, hardware key support provides better secu - Application access. Not supported: - + - Desktop access. - Legacy OpenSSH server access @@ -76,14 +76,14 @@ like `tctl edit`. With touch required, hardware key support provides better secu - Install a smart card driver for you operating system. Teleport clients will connect to your YubiKey through the smart card driver to generate keys and perform cryptographic operations. - - MacOS and Windows both ship with smart card drivers. + - macOS and Windows both ship with smart card drivers. - If you run into problems on Windows, try the official [YubiKey Smart Card Minidriver](https://www.yubico.com/support/download/smart-card-drivers-tools/). - - On Linux distributions, download the [YubiKey Manager or Yubico PIV tool](https://www.yubico.com/support/download/smart-card-drivers-tools/), which both include the Linux smart card driver as a dependency. + - On Linux distributions, download the [YubiKey Manager or Yubico PIV tool](https://www.yubico.com/support/download/smart-card-drivers-tools/), which both include the Linux smart card driver as a dependency. - (!docs/pages/includes/tctl.mdx!) ## Step 1/2. Enforce hardware key support -Hardware key support is not required by default. +Hardware key support is not required by default. There are three primary options for hardware key support: - `hardware_key`: User keys are stored on their hardware key without touch/PIN protection. A separate MFA check is used for @@ -131,10 +131,10 @@ cluster auth preference has been updated ## Step 2/2. Log in -After you configure a role or cluster to require a hardware key, all users signing in with that +After you configure a role or cluster to require a hardware key, all users signing in with that role or to that cluster must use their hardware key for all Teleport requests. -Affected users will be prompted to connect and touch their YubiKey to sign in. +Affected users will be prompted to connect and touch their YubiKey to sign in. The first time users sign in with their hardware key they might be required to immediately sign in again. @@ -201,9 +201,9 @@ $ tsh clusters # Relogging in with hardware-backed private key. # Enter password for Teleport user dev: # Tap your YubiKey -# Cluster Name Status Cluster Type Labels Selected -# ----------- ------ ------------ ------ -------- -# example.com online root * +# Cluster Name Status Cluster Type Labels Selected +# ----------- ------ ------------ ------ -------- +# example.com online root * ``` ## Custom PIV setup @@ -250,22 +250,22 @@ This can be done with the [YubiKey Manager CLI](https://developers.yubico.com/yu `ykman piv keys generate -a ECCP256 [slot] --touch-policy=[never|cached|always] --pin-policy=[never|once|always] -` -After running this command, you're prompted to enter your management key to complete the request. +After running this command, you're prompted to enter your management key to complete the request. Make sure that the touch and PIN policy satisfy the hardware key requirement for your cluster and roles. ## Troubleshooting ### `ERROR: private key policy not met` -This error is returned by the Auth and Proxy services if a user does not meet the required private key policy. -Both `tsh` and Teleport Connect automatically catch these errors and require the user to sign in again with a valid hardware-based private key. +This error is returned by the Auth and Proxy services if a user does not meet the required private key policy. +Both `tsh` and Teleport Connect automatically catch these errors and require the user to sign in again with a valid hardware-based private key. ### `ERROR: authenticating with management key: auth challenge: smart card error 6982: security status not satisfied` Smart card auth challenge errors can appear when the wrong management key is used. -Teleport clients expect a fresh PIV key with the default management key. -You can reset this key, along with any existing PIV keys and certificates, with the +Teleport clients expect a fresh PIV key with the default management key. +You can reset this key, along with any existing PIV keys and certificates, with the [YubiKey Manager CLI](https://developers.yubico.com/yubikey-manager/) `ykman piv reset`. If you want to use a different management key, follow the [custom PIV setup instructions](#custom-piv-setup). @@ -282,7 +282,7 @@ Yubikey for Hardware Key support, you might get an error on rare occasions. Depending on your settings, you might be asked to tap your Yubikey many times. Each tap is necessary to safely authenticate you. -For example, if you have `second_factor: webauthn` set in your `cluster_auth_preference`, +For example, if you have `second_factor: webauthn` set in your `cluster_auth_preference`, and `require_session_mfa: hardware_key_touch` set on your role, you'll see the following output when you first sign in: @@ -300,7 +300,7 @@ Detected security key tap Unmet private key policy "hardware_key_touch". -# At this point, `tsh` can infer from the error that the user's role requires +# At this point, `tsh` can infer from the error that the user's role requires # "hardware_key_touch", so it generates a private key directly on the hardware key # with a tap and re-initiates the sign in process. diff --git a/docs/pages/connect-your-client/introduction.mdx b/docs/pages/connect-your-client/introduction.mdx index efc32cb4f6a7f..233e5c2fe4e3e 100644 --- a/docs/pages/connect-your-client/introduction.mdx +++ b/docs/pages/connect-your-client/introduction.mdx @@ -116,8 +116,8 @@ server and database access within a single window. 1. Provide the address of your Teleport Cluster (e.g. `https://example.teleport.sh`) and click **NEXT**. -1. Teleport Connect will ask you for your username, password, and MFA. - +1. Teleport Connect will ask you for your username, password, and MFA. + If Teleport is integrated with an external Identity Provider (**IdP**), you might be prompted to authenticate with that service in a browser window. @@ -131,7 +131,7 @@ Teleport provides a web interface for users to interact with Teleport, e.g., by accessing resources or creating Access Requests. This is usually found at the same URL used to connect to Teleport with (e.g. `https://example.teleport.sh`), but you should confirm the Web UI URL with the team that manages your Teleport -deployment. +deployment. The Web UI provides similar access to resources as Teleport Connect, and additional access to to Request and Activity logs for users with the right @@ -148,8 +148,8 @@ permissions. ```code $ tsh ls -Node Name Address Labels -------------------- --------------- ---------------------------- +Node Name Address Labels +------------------- --------------- ---------------------------- server1.example.com 192.0.2.24:3022 access=servers,hostname=server1 server2.example.com 192.0.2.32:3022 access=servers,hostname=server2 ``` @@ -254,7 +254,7 @@ some-file.ext 7% |███████ | (25/342 MB, 2.9 MB/s) -Teleport Connect allows you to transfer files to a remote server and runs on MacOS, Linux and Windows. You can use the arrows in the top-left corner of an active SSH session to upload and download files: +Teleport Connect allows you to transfer files to a remote server and runs on macOS, Linux and Windows. You can use the arrows in the top-left corner of an active SSH session to upload and download files: ![Download with connect](../../img/download_files_connect.png) @@ -296,7 +296,7 @@ that database. For example, to connect to a MySQL or MariaDB database, you'll need the [MySQL CLI](https://dev.mysql.com/doc/refman/8.0/en/mysql.html) client: ```code -$ tsh db connect --db-user=alice --db-name=teleport_example mysql-server1 +$ tsh db connect --db-user=alice --db-name=teleport_example mysql-server1 ``` In this example, `teleport_example` is a pre-existing database on the MySQL server: @@ -358,9 +358,9 @@ list those that are accessible to your user under **Desktops**. 1. Next to the desktop you want to access, click **CONNECT**. Select @@ -386,4 +386,3 @@ either directly or through proxy tunnels. {/*lint ignore messaging for page title*/} - [Database Access GUI Clients](./gui-clients.mdx) details how to connect many popular database GUI clients through Teleport. - diff --git a/docs/pages/connect-your-client/teleport-connect.mdx b/docs/pages/connect-your-client/teleport-connect.mdx index 722886f08783a..91739ecb26aa4 100644 --- a/docs/pages/connect-your-client/teleport-connect.mdx +++ b/docs/pages/connect-your-client/teleport-connect.mdx @@ -446,6 +446,7 @@ Below is the list of the supported config properties. | `theme` | `system` | Color theme for the app. Available modes: `light`, `dark`, `system`. | | `terminal.fontFamily` | `Menlo, Monaco, monospace` on macOS
`Consolas, monospace` on Windows
`'Droid Sans Mono', monospace` on Linux | Font family for the terminal. | | `terminal.fontSize` | 15 | Font size for the terminal. | +| `terminal.windowsBackend` | `auto` | `auto` uses modern [ConPTY](https://devblogs.microsoft.com/commandline/windows-command-line-introducing-the-windows-pseudo-console-conpty/) system if available, which requires Windows 10 (19H1) or above. Set to `winpty` to use winpty even if ConPTY is available. | | `usageReporting.enabled` | `false` | Enables collecting anonymous usage data (see [Telemetry](#telemetry)). | | `keymap.tab1` - `keymap.tab9` | `Command+1` - `Command+9` on macOS
`Ctrl+1` - `Ctrl+9` on Windows
`Alt+1` - `Alt+9` on Linux | Shortcut to open tab 1–9. | | `keymap.closeTab` | `Command+W` on macOS
`Ctrl+Shift+W` on Windows/Linux | Shortcut to close a tab. | @@ -458,6 +459,7 @@ Below is the list of the supported config properties. | `keymap.openProfiles` | `Command+I` on macOS
`Ctrl+Shift+I` on Windows/Linux | Shortcut to open the profile selector. | | `keymap.openSearchBar` | `Command+K` on macOS
`Ctrl+Shift+K` on Windows/Linux | Shortcut to open the search bar. | | `headless.skipConfirm` | false | Skips the confirmation prompt for Headless WebAuthn approval and instead prompts for WebAuthn immediately. | +| `ssh.noResume` | false | Disables SSH connection resumption. | -Remove Teleport Connect for MacOS from the Applications directory with this command: +Remove Teleport Connect for macOS from the Applications directory with this command: ```code $ sudo rm -rf /Applications/Teleport\ Connect.app @@ -665,4 +667,3 @@ Installs based on a tarball should remove the
- diff --git a/docs/pages/deploy-a-cluster/deployments/aws-ha-autoscale-cluster-terraform.mdx b/docs/pages/deploy-a-cluster/deployments/aws-ha-autoscale-cluster-terraform.mdx index 69e2869ae0e9c..29bfb2d2ce075 100644 --- a/docs/pages/deploy-a-cluster/deployments/aws-ha-autoscale-cluster-terraform.mdx +++ b/docs/pages/deploy-a-cluster/deployments/aws-ha-autoscale-cluster-terraform.mdx @@ -29,7 +29,7 @@ Fedora/CentOS: `yum -y install awscli` Ubuntu/Debian: `apt-get -y install awscli` -MacOS (with [Homebrew](https://brew.sh/)): `brew install awscli` +macOS (with [Homebrew](https://brew.sh/)): `brew install awscli` When possible, installing via a package is always preferable. If you can't find a package available for your distribution, you can also download the tool from [https://aws.amazon.com/cli/](https://aws.amazon.com/cli/) @@ -162,7 +162,7 @@ Teleport (Gravitational) automatically builds and publishes Teleport Community E AMIs when we release a new version of Teleport. The AMI names follow the format: `teleport---` where `` is either `oss` or `ent` (Enterprise), `` is the version of Teleport, e.g. `(=teleport.version=)`, and `` is either `x86_64` or `arm64`. - + FIPS 140-2 compatible AMIs (which deploy Teleport in FIPS 140-2 mode by default) have the `-fips` suffix after ``, e.g. `teleport-ent-(=teleport.version=)-x86_64-fips`. @@ -867,4 +867,3 @@ $ ./connect.sh node ### AWS quotas (!docs/pages/includes/aws-quotas.mdx!) - diff --git a/docs/pages/deploy-a-cluster/deployments/aws-starter-cluster-terraform.mdx b/docs/pages/deploy-a-cluster/deployments/aws-starter-cluster-terraform.mdx index ec10150898403..f50f7b06bd1e5 100644 --- a/docs/pages/deploy-a-cluster/deployments/aws-starter-cluster-terraform.mdx +++ b/docs/pages/deploy-a-cluster/deployments/aws-starter-cluster-terraform.mdx @@ -42,7 +42,7 @@ Fedora/CentOS: `yum -y install awscli` Ubuntu/Debian: `apt-get -y install awscli` -MacOS (with [Homebrew](https://brew.sh/)): `brew install awscli` +macOS (with [Homebrew](https://brew.sh/)): `brew install awscli` When possible, installing via a package is always preferable. If you can't find a package available for your distribution, you can also download the tool from [https://aws.amazon.com/cli/](https://aws.amazon.com/cli/) @@ -733,4 +733,3 @@ To add new nodes/EC2 servers that you can "SSH into" you'll need to: ### AWS quotas (!docs/pages/includes/aws-quotas.mdx!) - diff --git a/docs/pages/enroll-resources/database-access/reference/cli.mdx b/docs/pages/enroll-resources/database-access/reference/cli.mdx index c9650a23a4faa..71b1f777d788c 100644 --- a/docs/pages/enroll-resources/database-access/reference/cli.mdx +++ b/docs/pages/enroll-resources/database-access/reference/cli.mdx @@ -127,7 +127,7 @@ $ teleport db configure create \ | `--protocol` | Proxied database protocol. Refer to the [configuration](./configuration.mdx#database-service-configuration) reference for supported values. | | `--uri` | Address the proxied database is reachable at. | | `--labels` | Comma-separated list of labels for the database, for example env=dev,dept=it | -| `-o/--output` | Write to stdout with `-o=stdout`, the default config file with `-o=file`, or a custom path with `-o=file:///path` | +| `-o/--output` | Write to stdout with `--output=stdout`, the default config file with `--output=file`, or a custom path with `--output=file:///path` | | `--dynamic-resources-labels` | Comma-separated list(s) of labels to match dynamic resources, for example env=dev,dept=it. Required to enable dynamic resources matching. | ## teleport db configure bootstrap diff --git a/docs/pages/enroll-resources/machine-id/reference/configuration.mdx b/docs/pages/enroll-resources/machine-id/reference/configuration.mdx index 8b16107be33eb..52c03b4dec839 100644 --- a/docs/pages/enroll-resources/machine-id/reference/configuration.mdx +++ b/docs/pages/enroll-resources/machine-id/reference/configuration.mdx @@ -1,6 +1,7 @@ --- title: Machine ID Configuration Reference description: Configuration reference for Teleport Machine ID. +tocDepth: 4 --- This reference documents the various options that can be configured in the `tbot` @@ -543,6 +544,150 @@ destination: - `v1.sock`: the Unix socket that the multiplexer listens on. - `agent.sock`: the Unix socket that the SSH agent listens on. +##### Using the SSH multiplexer programmatically + +To use the SSH multiplexer programmatically, your SSH client library will need +to support one of two things: + +- The ability to use a ProxyCommand with FDPass. If so, you can use the + `ssh_config` file generated by `tbot` to configure the SSH client. +- The ability to accept an open socket to use as the connection to the SSH + server. You will then need to manually connect to the socket and send the + multiplexer request. + +The `v1.sock` Unix Domain Socket implements the V1 Teleport SSH multiplexer +protocol. The client must first send a short request message to indicate the +desired target host and port, terminated with a null byte. The multiplexer will +then begin to forward traffic to the target host and port. The client can then +make an SSH connection. + +
+```python +import os +import paramiko +import socket + +host = "ubuntu.example.teleport.sh" +username = "root" +port = 3022 +directory_destination = "/opt/machine-id" + +# Connect to Mux Unix Domain Socket +sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) +sock.connect(os.path.join(directory_destination, "v1.sock")) +# Send the connection request specifying the server you wish to connect to +sock.sendall(f"{host}:{port}\x00".encode("utf-8")) + +# We must set the env var as Paramiko does not make this configurable... +os.environ["SSH_AUTH_SOCK"] = os.path.join(directory_destination, "agent.sock") + +ssh_config = paramiko.SSHConfig() +with open(os.path.join(directory_destination, "ssh_config")) as f: + ssh_config.parse(f) + +ssh_client = paramiko.SSHClient() + +# Paramiko does not support known_hosts with CAs: https://github.com/paramiko/paramiko/issues/771 +# Therefore, we must disable host key checking +ssh_client.set_missing_host_key_policy(paramiko.WarningPolicy()) + +ssh_client.connect( + hostname=host, + port=port, + username=username, + sock=sock +) + +stdin, stdout, stderr = ssh_client.exec_command("hostname") +print(stdout.read().decode()) +``` +
+ +
+```go +package main + +import ( + "fmt" + "net" + "path/filepath" + + "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/agent" + "golang.org/x/crypto/ssh/knownhosts" +) + +func main() { + host := "ubuntu.example.teleport.sh" + username := "root" + directoryDestination := "/opt/machine-id" + + // Setup Agent and Known Hosts + agentConn, err := net.Dial( + "unix", filepath.Join(directoryDestination, "agent.sock"), + ) + if err != nil { + panic(err) + } + defer agentConn.Close() + agentClient := agent.NewClient(agentConn) + hostKeyCallback, err := knownhosts.New( + filepath.Join(directoryDestination, "known_hosts"), + ) + if err != nil { + panic(err) + } + + // Create SSH Config + sshConfig := &ssh.ClientConfig{ + Auth: []ssh.AuthMethod{ + ssh.PublicKeysCallback(agentClient.Signers), + }, + User: username, + HostKeyCallback: hostKeyCallback, + } + + // Dial Unix Domain Socket and send multiplexing request + conn, err := net.Dial( + "unix", filepath.Join(directoryDestination, "v1.sock"), + ) + if err != nil { + panic(err) + } + defer conn.Close() + _, err = fmt.Fprint(conn, fmt.Sprintf("%s:0\x00", host)) + if err != nil { + panic(err) + } + + sshConn, sshChan, sshReq, err := ssh.NewClientConn( + conn, + // Port here doesn't matter because Multiplexer has already established + // connection. + fmt.Sprintf("%s:22", host), + sshConfig, + ) + if err != nil { + panic(err) + } + sshClient := ssh.NewClient(sshConn, sshChan, sshReq) + defer sshClient.Close() + + sshSess, err := sshClient.NewSession() + if err != nil { + panic(err) + } + defer sshSess.Close() + + out, err := sshSess.CombinedOutput("hostname") + if err != nil { + panic(err) + } + fmt.Println(string(out)) +} +``` +
+ ### Destinations A destination is somewhere that `tbot` can read and write artifacts. diff --git a/docs/pages/enroll-resources/server-access/guides/jetbrains-sftp.mdx b/docs/pages/enroll-resources/server-access/guides/jetbrains-sftp.mdx index 8af626cba7b78..c9304a513d426 100644 --- a/docs/pages/enroll-resources/server-access/guides/jetbrains-sftp.mdx +++ b/docs/pages/enroll-resources/server-access/guides/jetbrains-sftp.mdx @@ -35,7 +35,7 @@ Append the resulting configuration snippet into your SSH config file located in the path below: - + `$HOME/.ssh/config` diff --git a/docs/pages/enroll-resources/server-access/guides/vscode.mdx b/docs/pages/enroll-resources/server-access/guides/vscode.mdx index 8f28b651f64f2..c0d062d9d8a03 100644 --- a/docs/pages/enroll-resources/server-access/guides/vscode.mdx +++ b/docs/pages/enroll-resources/server-access/guides/vscode.mdx @@ -18,7 +18,7 @@ This guide explains how to use Teleport and Visual Studio Code's remote SSH exte [Server Access Getting Started Guide](../getting-started.mdx) to learn how. -Linux and MacOS clients should rely on their operating system-provided OpenSSH +Linux and macOS clients should rely on their operating system-provided OpenSSH packages. Windows 10 clients should refer to Microsoft's [OpenSSH guide][win10]; older clients can use `ssh.exe` from either [Git for Windows][git] or Microsoft's [Win32-OpenSSH project][win32-openssh]. @@ -40,7 +40,7 @@ Append the resulting configuration snippet into your SSH config file located in the path below: - + `$HOME/.ssh/config` @@ -86,7 +86,7 @@ When you see this error, re-run `tsh login` to refresh your local certificate. ## Step 2/3. Configure Visual Studio Code -Install the [Remote - SSH extension][remote-ssh] in your local VS Code instance. +Install the [Remote - SSH extension][remote-ssh] in your local VS Code instance. A new "Window Indicator" (icon with two arrows) should appear in the bottom left of your VS Code window.
diff --git a/docs/pages/enroll-resources/server-access/openssh/openssh-manual-install.mdx b/docs/pages/enroll-resources/server-access/openssh/openssh-manual-install.mdx index 20a47c23f1e8c..9acb1bc205873 100644 --- a/docs/pages/enroll-resources/server-access/openssh/openssh-manual-install.mdx +++ b/docs/pages/enroll-resources/server-access/openssh/openssh-manual-install.mdx @@ -67,7 +67,7 @@ In this setup, the Teleport SSH Service performs RBAC checks as well as audits a configured. This must be done *before* your Teleport cluster is upgraded to Teleport 14. If you are having issues registering OpenSSH nodes or need to upgrade your - Teleport cluster to Teleport 14 before registering all of your OpenSSH nodes, you can + Teleport cluster to Teleport 14 before registering all of your OpenSSH nodes, you can pass the `TELEPORT_UNSTABLE_UNLISTED_AGENT_DIALING` environment variable to your Proxy Service and set it to `yes`. This will allow connections to unregistered OpenSSH nodes but will be removed in Teleport v15. @@ -102,7 +102,7 @@ The `metadata.labels` field labels the SSH Service instance so you can apply RBA The `metadata.name` field isn't mandatory, but setting it here will save you some work later. To generate a new universal unique identifier (UUID) suitable for a `node` name, use the `uuidgen` -on Linux or MacOS, or use the `New-Guid` cmdlet in Powershell on Windows. +on Linux or macOS, or use the `New-Guid` cmdlet in Powershell on Windows. Create the node resource: @@ -540,5 +540,3 @@ $ ssh -F ssh_config_teleport ${USER?}@node2.leafcluster.${CLUSTER} qualified domain name, rather than an IP address. - - diff --git a/docs/pages/management/admin/uninstall-teleport.mdx b/docs/pages/management/admin/uninstall-teleport.mdx index f327d4e6d03a0..52531ec9ff59c 100644 --- a/docs/pages/management/admin/uninstall-teleport.mdx +++ b/docs/pages/management/admin/uninstall-teleport.mdx @@ -44,7 +44,7 @@ $ docker stop teleport $ sudo killall teleport ``` - + Instruct `launchd` to stop the Teleport process, and disable it from automatically starting: @@ -169,8 +169,8 @@ Follow the instructions for your Linux distribution: $ sudo rm -f /usr/local/bin/tsh ``` - - If you installed the MacOS `tsh` client-only package and/or Teleport Connect for MacOS, you can optionally remove those too: + + If you installed the macOS `tsh` client-only package and/or Teleport Connect for macOS, you can optionally remove those too: ```code $ sudo rm -rf /Applications/tsh.app diff --git a/docs/pages/reference/terraform-provider.mdx b/docs/pages/reference/terraform-provider.mdx index 989079233135c..a54926db2150a 100644 --- a/docs/pages/reference/terraform-provider.mdx +++ b/docs/pages/reference/terraform-provider.mdx @@ -3,6 +3,9 @@ title: "Teleport Terraform Provider" description: Reference documentation of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + The Teleport Terraform provider allows Terraform users to configure Teleport from Terraform. @@ -127,13 +130,15 @@ This auth method has the following limitations: ### Optional -- `addr` (String) host:port where Teleport Auth Service is running. This can also be set with the environment variable `TF_TELEPORT_ADDR`. +- `addr` (String) host:port of the Teleport address. This can be the Teleport Proxy Service address (port 443 or 4080) or the Teleport Auth Service address (port 3025). This can also be set with the environment variable `TF_TELEPORT_ADDR`. - `cert_base64` (String) Base64 encoded TLS auth certificate. This can also be set with the environment variable `TF_TELEPORT_CERT_BASE64`. - `cert_path` (String) Path to Teleport auth certificate file. This can also be set with the environment variable `TF_TELEPORT_CERT`. - `dial_timeout_duration` (String) DialTimeout sets timeout when trying to connect to the server. This can also be set with the environment variable `TF_TELEPORT_DIAL_TIMEOUT_DURATION`. - `identity_file` (String, Sensitive) Teleport identity file content. This can also be set with the environment variable `TF_TELEPORT_IDENTITY_FILE`. - `identity_file_base64` (String, Sensitive) Teleport identity file content base64 encoded. This can also be set with the environment variable `TF_TELEPORT_IDENTITY_FILE_BASE64`. - `identity_file_path` (String) Teleport identity file path. This can also be set with the environment variable `TF_TELEPORT_IDENTITY_FILE_PATH`. +- `join_method` (String) Enables the native Terraform MachineID support. When set, Terraform uses MachineID to securely join the Teleport cluster and obtain credentials. See [the join method reference](./join-methods.mdx) for possible values, you must use [a delegated join method](./join-methods.mdx#secret-vs-delegated). This can also be set with the environment variable `TF_TELEPORT_JOIN_METHOD`. +- `join_token` (String) Name of the token used for the native MachineID joining. This value is not sensitive for [delegated join methods](./join-methods.mdx#secret-vs-delegated). This can also be set with the environment variable `TF_TELEPORT_JOIN_TOKEN`. - `key_base64` (String, Sensitive) Base64 encoded TLS auth key. This can also be set with the environment variable `TF_TELEPORT_KEY_BASE64`. - `key_path` (String) Path to Teleport auth key file. This can also be set with the environment variable `TF_TELEPORT_KEY`. - `profile_dir` (String) Teleport profile path. This can also be set with the environment variable `TF_TELEPORT_PROFILE_PATH`. diff --git a/docs/pages/reference/terraform-provider/data-sources.mdx b/docs/pages/reference/terraform-provider/data-sources.mdx index 54ddfa4cc2b95..a6ba9b9596a17 100644 --- a/docs/pages/reference/terraform-provider/data-sources.mdx +++ b/docs/pages/reference/terraform-provider/data-sources.mdx @@ -3,6 +3,9 @@ title: "Terraform data-sources index" description: "Index of all the data-sources supported by the Teleport Terraform Provider" --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + {/* This file will be renamed data-sources.mdx during build time. The template name is reserved by tfplugindocs so we suffix with -index. diff --git a/docs/pages/reference/terraform-provider/data-sources/access_list.mdx b/docs/pages/reference/terraform-provider/data-sources/access_list.mdx index 1e25ce89465fe..87075e1b269a7 100644 --- a/docs/pages/reference/terraform-provider/data-sources/access_list.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/access_list.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_access_list Terraform data-source description: This page describes the supported values of the teleport_access_list data-source of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + diff --git a/docs/pages/reference/terraform-provider/data-sources/app.mdx b/docs/pages/reference/terraform-provider/data-sources/app.mdx index a27bb41053328..dbed5bb7f1a68 100644 --- a/docs/pages/reference/terraform-provider/data-sources/app.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/app.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_app Terraform data-source description: This page describes the supported values of the teleport_app data-source of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + diff --git a/docs/pages/reference/terraform-provider/data-sources/auth_preference.mdx b/docs/pages/reference/terraform-provider/data-sources/auth_preference.mdx index 038773e72d34b..885fc3daa482c 100644 --- a/docs/pages/reference/terraform-provider/data-sources/auth_preference.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/auth_preference.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_auth_preference Terraform data-source description: This page describes the supported values of the teleport_auth_preference data-source of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + diff --git a/docs/pages/reference/terraform-provider/data-sources/cluster_maintenance_config.mdx b/docs/pages/reference/terraform-provider/data-sources/cluster_maintenance_config.mdx index d3990eccef595..a1de281b7b8ec 100644 --- a/docs/pages/reference/terraform-provider/data-sources/cluster_maintenance_config.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/cluster_maintenance_config.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_cluster_maintenance_config Terraform data-sour description: This page describes the supported values of the teleport_cluster_maintenance_config data-source of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + diff --git a/docs/pages/reference/terraform-provider/data-sources/cluster_networking_config.mdx b/docs/pages/reference/terraform-provider/data-sources/cluster_networking_config.mdx index b87bbdf4fa33e..32af2beba8f5b 100644 --- a/docs/pages/reference/terraform-provider/data-sources/cluster_networking_config.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/cluster_networking_config.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_cluster_networking_config Terraform data-sourc description: This page describes the supported values of the teleport_cluster_networking_config data-source of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + diff --git a/docs/pages/reference/terraform-provider/data-sources/database.mdx b/docs/pages/reference/terraform-provider/data-sources/database.mdx index 0bbb5020b4d79..7220f84418faa 100644 --- a/docs/pages/reference/terraform-provider/data-sources/database.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/database.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_database Terraform data-source description: This page describes the supported values of the teleport_database data-source of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + diff --git a/docs/pages/reference/terraform-provider/data-sources/github_connector.mdx b/docs/pages/reference/terraform-provider/data-sources/github_connector.mdx index d9c31c5408cd4..faf4d97151e53 100644 --- a/docs/pages/reference/terraform-provider/data-sources/github_connector.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/github_connector.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_github_connector Terraform data-source description: This page describes the supported values of the teleport_github_connector data-source of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + diff --git a/docs/pages/reference/terraform-provider/data-sources/login_rule.mdx b/docs/pages/reference/terraform-provider/data-sources/login_rule.mdx index b23bed37d6668..49c4d6957c251 100644 --- a/docs/pages/reference/terraform-provider/data-sources/login_rule.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/login_rule.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_login_rule Terraform data-source description: This page describes the supported values of the teleport_login_rule data-source of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + diff --git a/docs/pages/reference/terraform-provider/data-sources/oidc_connector.mdx b/docs/pages/reference/terraform-provider/data-sources/oidc_connector.mdx index 758f1076c9236..57b7098854dec 100644 --- a/docs/pages/reference/terraform-provider/data-sources/oidc_connector.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/oidc_connector.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_oidc_connector Terraform data-source description: This page describes the supported values of the teleport_oidc_connector data-source of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + diff --git a/docs/pages/reference/terraform-provider/data-sources/okta_import_rule.mdx b/docs/pages/reference/terraform-provider/data-sources/okta_import_rule.mdx index f346735c66237..8cb23b07f41e6 100644 --- a/docs/pages/reference/terraform-provider/data-sources/okta_import_rule.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/okta_import_rule.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_okta_import_rule Terraform data-source description: This page describes the supported values of the teleport_okta_import_rule data-source of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + diff --git a/docs/pages/reference/terraform-provider/data-sources/provision_token.mdx b/docs/pages/reference/terraform-provider/data-sources/provision_token.mdx index 79d77980ab8d6..0af4e694cd8e0 100644 --- a/docs/pages/reference/terraform-provider/data-sources/provision_token.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/provision_token.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_provision_token Terraform data-source description: This page describes the supported values of the teleport_provision_token data-source of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + @@ -36,7 +39,7 @@ Optional: - `gcp` (Attributes) GCP allows the configuration of options specific to the "gcp" join method. (see [below for nested schema](#nested-schema-for-specgcp)) - `github` (Attributes) GitHub allows the configuration of options specific to the "github" join method. (see [below for nested schema](#nested-schema-for-specgithub)) - `gitlab` (Attributes) GitLab allows the configuration of options specific to the "gitlab" join method. (see [below for nested schema](#nested-schema-for-specgitlab)) -- `join_method` (String) JoinMethod is the joining method required in order to use this token. Supported joining methods include "token", "ec2", and "iam". +- `join_method` (String) JoinMethod is the joining method required in order to use this token. Supported joining methods include: azure, circleci, ec2, gcp, github, gitlab, iam, kubernetes, spacelift, token, tpm - `kubernetes` (Attributes) Kubernetes allows the configuration of options specific to the "kubernetes" join method. (see [below for nested schema](#nested-schema-for-speckubernetes)) - `spacelift` (Attributes) Spacelift allows the configuration of options specific to the "spacelift" join method. (see [below for nested schema](#nested-schema-for-specspacelift)) - `suggested_agent_matcher_labels` (Map of List of String) diff --git a/docs/pages/reference/terraform-provider/data-sources/role.mdx b/docs/pages/reference/terraform-provider/data-sources/role.mdx index d58f38e87d664..694f0276075bd 100644 --- a/docs/pages/reference/terraform-provider/data-sources/role.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/role.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_role Terraform data-source description: This page describes the supported values of the teleport_role data-source of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + diff --git a/docs/pages/reference/terraform-provider/data-sources/saml_connector.mdx b/docs/pages/reference/terraform-provider/data-sources/saml_connector.mdx index 65935a4bb9589..bcf70b19659a3 100644 --- a/docs/pages/reference/terraform-provider/data-sources/saml_connector.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/saml_connector.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_saml_connector Terraform data-source description: This page describes the supported values of the teleport_saml_connector data-source of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + diff --git a/docs/pages/reference/terraform-provider/data-sources/session_recording_config.mdx b/docs/pages/reference/terraform-provider/data-sources/session_recording_config.mdx index f5d2eed0523d7..f050c0a0258a4 100644 --- a/docs/pages/reference/terraform-provider/data-sources/session_recording_config.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/session_recording_config.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_session_recording_config Terraform data-source description: This page describes the supported values of the teleport_session_recording_config data-source of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + diff --git a/docs/pages/reference/terraform-provider/data-sources/trusted_cluster.mdx b/docs/pages/reference/terraform-provider/data-sources/trusted_cluster.mdx index 8280b57bda6e2..869288ced0477 100644 --- a/docs/pages/reference/terraform-provider/data-sources/trusted_cluster.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/trusted_cluster.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_trusted_cluster Terraform data-source description: This page describes the supported values of the teleport_trusted_cluster data-source of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + diff --git a/docs/pages/reference/terraform-provider/data-sources/trusted_device.mdx b/docs/pages/reference/terraform-provider/data-sources/trusted_device.mdx index a13685ac86de2..062240212cbd7 100644 --- a/docs/pages/reference/terraform-provider/data-sources/trusted_device.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/trusted_device.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_trusted_device Terraform data-source description: This page describes the supported values of the teleport_trusted_device data-source of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + diff --git a/docs/pages/reference/terraform-provider/data-sources/user.mdx b/docs/pages/reference/terraform-provider/data-sources/user.mdx index 35b4b96053ec2..b2b12619f9268 100644 --- a/docs/pages/reference/terraform-provider/data-sources/user.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/user.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_user Terraform data-source description: This page describes the supported values of the teleport_user data-source of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + diff --git a/docs/pages/reference/terraform-provider/resources.mdx b/docs/pages/reference/terraform-provider/resources.mdx index a96c93026b7d4..6f9fc6cdbf39b 100644 --- a/docs/pages/reference/terraform-provider/resources.mdx +++ b/docs/pages/reference/terraform-provider/resources.mdx @@ -3,6 +3,9 @@ title: "Terraform resources index" description: "Index of all the datasources supported by the Teleport Terraform Provider" --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + {/* This file will be renamed data-sources.mdx during build time. The template name is reserved by tfplugindocs so we suffix with -index. diff --git a/docs/pages/reference/terraform-provider/resources/access_list.mdx b/docs/pages/reference/terraform-provider/resources/access_list.mdx index 75793d49c3c2d..952e44b2e24b9 100644 --- a/docs/pages/reference/terraform-provider/resources/access_list.mdx +++ b/docs/pages/reference/terraform-provider/resources/access_list.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_access_list Terraform resource description: This page describes the supported values of the teleport_access_list resource of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + ## Example Usage diff --git a/docs/pages/reference/terraform-provider/resources/app.mdx b/docs/pages/reference/terraform-provider/resources/app.mdx index bb8e279f3d783..b83cf4b595a74 100644 --- a/docs/pages/reference/terraform-provider/resources/app.mdx +++ b/docs/pages/reference/terraform-provider/resources/app.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_app Terraform resource description: This page describes the supported values of the teleport_app resource of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + ## Example Usage diff --git a/docs/pages/reference/terraform-provider/resources/auth_preference.mdx b/docs/pages/reference/terraform-provider/resources/auth_preference.mdx index f096b5b1eb3a7..7d40155aadee1 100644 --- a/docs/pages/reference/terraform-provider/resources/auth_preference.mdx +++ b/docs/pages/reference/terraform-provider/resources/auth_preference.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_auth_preference Terraform resource description: This page describes the supported values of the teleport_auth_preference resource of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + ## Example Usage diff --git a/docs/pages/reference/terraform-provider/resources/bot.mdx b/docs/pages/reference/terraform-provider/resources/bot.mdx index 0ef1cbb5cddb1..d8144e8ca2ce3 100644 --- a/docs/pages/reference/terraform-provider/resources/bot.mdx +++ b/docs/pages/reference/terraform-provider/resources/bot.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_bot Terraform resource description: This page describes the supported values of the teleport_bot resource of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + ## Example Usage diff --git a/docs/pages/reference/terraform-provider/resources/cluster_maintenance_config.mdx b/docs/pages/reference/terraform-provider/resources/cluster_maintenance_config.mdx index 36337fab3d9e3..dd19b42583877 100644 --- a/docs/pages/reference/terraform-provider/resources/cluster_maintenance_config.mdx +++ b/docs/pages/reference/terraform-provider/resources/cluster_maintenance_config.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_cluster_maintenance_config Terraform resource description: This page describes the supported values of the teleport_cluster_maintenance_config resource of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + ## Example Usage diff --git a/docs/pages/reference/terraform-provider/resources/cluster_networking_config.mdx b/docs/pages/reference/terraform-provider/resources/cluster_networking_config.mdx index 34bc6818c2d2f..19d9b4379dc2d 100644 --- a/docs/pages/reference/terraform-provider/resources/cluster_networking_config.mdx +++ b/docs/pages/reference/terraform-provider/resources/cluster_networking_config.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_cluster_networking_config Terraform resource description: This page describes the supported values of the teleport_cluster_networking_config resource of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + ## Example Usage diff --git a/docs/pages/reference/terraform-provider/resources/database.mdx b/docs/pages/reference/terraform-provider/resources/database.mdx index f81e6d425d934..c38f58d0c0217 100644 --- a/docs/pages/reference/terraform-provider/resources/database.mdx +++ b/docs/pages/reference/terraform-provider/resources/database.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_database Terraform resource description: This page describes the supported values of the teleport_database resource of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + ## Example Usage diff --git a/docs/pages/reference/terraform-provider/resources/github_connector.mdx b/docs/pages/reference/terraform-provider/resources/github_connector.mdx index 0c1ef471f1850..6b532e77edb2c 100644 --- a/docs/pages/reference/terraform-provider/resources/github_connector.mdx +++ b/docs/pages/reference/terraform-provider/resources/github_connector.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_github_connector Terraform resource description: This page describes the supported values of the teleport_github_connector resource of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + ## Example Usage diff --git a/docs/pages/reference/terraform-provider/resources/login_rule.mdx b/docs/pages/reference/terraform-provider/resources/login_rule.mdx index 28c6b4de1df92..152ccf0e7de67 100644 --- a/docs/pages/reference/terraform-provider/resources/login_rule.mdx +++ b/docs/pages/reference/terraform-provider/resources/login_rule.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_login_rule Terraform resource description: This page describes the supported values of the teleport_login_rule resource of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + ## Example Usage diff --git a/docs/pages/reference/terraform-provider/resources/oidc_connector.mdx b/docs/pages/reference/terraform-provider/resources/oidc_connector.mdx index 1b49205baa12c..463818acf2fcc 100644 --- a/docs/pages/reference/terraform-provider/resources/oidc_connector.mdx +++ b/docs/pages/reference/terraform-provider/resources/oidc_connector.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_oidc_connector Terraform resource description: This page describes the supported values of the teleport_oidc_connector resource of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + ## Example Usage diff --git a/docs/pages/reference/terraform-provider/resources/okta_import_rule.mdx b/docs/pages/reference/terraform-provider/resources/okta_import_rule.mdx index 16a0171ecfb12..50ca7319ea0e9 100644 --- a/docs/pages/reference/terraform-provider/resources/okta_import_rule.mdx +++ b/docs/pages/reference/terraform-provider/resources/okta_import_rule.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_okta_import_rule Terraform resource description: This page describes the supported values of the teleport_okta_import_rule resource of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + ## Example Usage diff --git a/docs/pages/reference/terraform-provider/resources/provision_token.mdx b/docs/pages/reference/terraform-provider/resources/provision_token.mdx index b10762a3f6f4b..e0c8150ba77da 100644 --- a/docs/pages/reference/terraform-provider/resources/provision_token.mdx +++ b/docs/pages/reference/terraform-provider/resources/provision_token.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_provision_token Terraform resource description: This page describes the supported values of the teleport_provision_token resource of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + ## Example Usage @@ -70,7 +73,7 @@ Optional: - `gcp` (Attributes) GCP allows the configuration of options specific to the "gcp" join method. (see [below for nested schema](#nested-schema-for-specgcp)) - `github` (Attributes) GitHub allows the configuration of options specific to the "github" join method. (see [below for nested schema](#nested-schema-for-specgithub)) - `gitlab` (Attributes) GitLab allows the configuration of options specific to the "gitlab" join method. (see [below for nested schema](#nested-schema-for-specgitlab)) -- `join_method` (String) JoinMethod is the joining method required in order to use this token. Supported joining methods include "token", "ec2", and "iam". +- `join_method` (String) JoinMethod is the joining method required in order to use this token. Supported joining methods include: azure, circleci, ec2, gcp, github, gitlab, iam, kubernetes, spacelift, token, tpm - `kubernetes` (Attributes) Kubernetes allows the configuration of options specific to the "kubernetes" join method. (see [below for nested schema](#nested-schema-for-speckubernetes)) - `spacelift` (Attributes) Spacelift allows the configuration of options specific to the "spacelift" join method. (see [below for nested schema](#nested-schema-for-specspacelift)) - `suggested_agent_matcher_labels` (Map of List of String) diff --git a/docs/pages/reference/terraform-provider/resources/role.mdx b/docs/pages/reference/terraform-provider/resources/role.mdx index fe7c262974923..d75a4d72cbd61 100644 --- a/docs/pages/reference/terraform-provider/resources/role.mdx +++ b/docs/pages/reference/terraform-provider/resources/role.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_role Terraform resource description: This page describes the supported values of the teleport_role resource of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + ## Example Usage diff --git a/docs/pages/reference/terraform-provider/resources/saml_connector.mdx b/docs/pages/reference/terraform-provider/resources/saml_connector.mdx index de7b03ceeab53..35a33e15e4244 100644 --- a/docs/pages/reference/terraform-provider/resources/saml_connector.mdx +++ b/docs/pages/reference/terraform-provider/resources/saml_connector.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_saml_connector Terraform resource description: This page describes the supported values of the teleport_saml_connector resource of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + ## Example Usage diff --git a/docs/pages/reference/terraform-provider/resources/server.mdx b/docs/pages/reference/terraform-provider/resources/server.mdx index 551d6c8a05190..81080c0b392c9 100644 --- a/docs/pages/reference/terraform-provider/resources/server.mdx +++ b/docs/pages/reference/terraform-provider/resources/server.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_server Terraform resource description: This page describes the supported values of the teleport_server resource of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + ## Example Usage diff --git a/docs/pages/reference/terraform-provider/resources/session_recording_config.mdx b/docs/pages/reference/terraform-provider/resources/session_recording_config.mdx index 6a2b47327ccde..a1453c83379d6 100644 --- a/docs/pages/reference/terraform-provider/resources/session_recording_config.mdx +++ b/docs/pages/reference/terraform-provider/resources/session_recording_config.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_session_recording_config Terraform resource description: This page describes the supported values of the teleport_session_recording_config resource of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + ## Example Usage diff --git a/docs/pages/reference/terraform-provider/resources/trusted_cluster.mdx b/docs/pages/reference/terraform-provider/resources/trusted_cluster.mdx index 3aa2b5d02abe2..46d6603eab449 100644 --- a/docs/pages/reference/terraform-provider/resources/trusted_cluster.mdx +++ b/docs/pages/reference/terraform-provider/resources/trusted_cluster.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_trusted_cluster Terraform resource description: This page describes the supported values of the teleport_trusted_cluster resource of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + ## Example Usage diff --git a/docs/pages/reference/terraform-provider/resources/trusted_device.mdx b/docs/pages/reference/terraform-provider/resources/trusted_device.mdx index 98c3e4fed0d7c..8887d04300911 100644 --- a/docs/pages/reference/terraform-provider/resources/trusted_device.mdx +++ b/docs/pages/reference/terraform-provider/resources/trusted_device.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_trusted_device Terraform resource description: This page describes the supported values of the teleport_trusted_device resource of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + ## Example Usage diff --git a/docs/pages/reference/terraform-provider/resources/user.mdx b/docs/pages/reference/terraform-provider/resources/user.mdx index 2c309194c73f5..f65fd9bb950db 100644 --- a/docs/pages/reference/terraform-provider/resources/user.mdx +++ b/docs/pages/reference/terraform-provider/resources/user.mdx @@ -3,6 +3,9 @@ title: Reference for the teleport_user Terraform resource description: This page describes the supported values of the teleport_user resource of the Teleport Terraform provider. --- +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/} + ## Example Usage diff --git a/e b/e index 6b810ddd77233..c725cd98a5c1f 160000 --- a/e +++ b/e @@ -1 +1 @@ -Subproject commit 6b810ddd7723329e8c81ccb6eb4d0d7e7c3db4a3 +Subproject commit c725cd98a5c1f3040297b9877a93f2fce9a922dd diff --git a/examples/chart/event-handler/templates/deployment.yaml b/examples/chart/event-handler/templates/deployment.yaml index 2cc2b8bc705cf..368b0015ab7a1 100644 --- a/examples/chart/event-handler/templates/deployment.yaml +++ b/examples/chart/event-handler/templates/deployment.yaml @@ -35,7 +35,7 @@ spec: image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} command: - - /usr/local/bin/teleport-event-handler + - /usr/local/bin/teleport-plugin - start - "--config" - "/etc/teleport-event-handler.toml" diff --git a/examples/chart/event-handler/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/event-handler/tests/__snapshot__/deployment_test.yaml.snap index 40822ff6114f3..d7a6dc7a36b7a 100644 --- a/examples/chart/event-handler/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/event-handler/tests/__snapshot__/deployment_test.yaml.snap @@ -24,7 +24,7 @@ should match the snapshot: spec: containers: - command: - - /usr/local/bin/teleport-event-handler + - /usr/local/bin/teleport-plugin - start - --config - /etc/teleport-event-handler.toml @@ -73,7 +73,7 @@ should mount tls.existingCASecretName and set environment when set in values: 1: | containers: - command: - - /usr/local/bin/teleport-event-handler + - /usr/local/bin/teleport-plugin - start - --config - /etc/teleport-event-handler.toml diff --git a/examples/chart/teleport-cluster/charts/teleport-operator/operator-crds/resources.teleport.dev_provisiontokens.yaml b/examples/chart/teleport-cluster/charts/teleport-operator/operator-crds/resources.teleport.dev_provisiontokens.yaml index edd501d22255c..1f733e28928ee 100644 --- a/examples/chart/teleport-cluster/charts/teleport-operator/operator-crds/resources.teleport.dev_provisiontokens.yaml +++ b/examples/chart/teleport-cluster/charts/teleport-operator/operator-crds/resources.teleport.dev_provisiontokens.yaml @@ -257,9 +257,9 @@ spec: type: string type: object join_method: - description: JoinMethod is the joining method required in order to - use this token. Supported joining methods include "token", "ec2", - and "iam". + description: 'JoinMethod is the joining method required in order to + use this token. Supported joining methods include: azure, circleci, + ec2, gcp, github, gitlab, iam, kubernetes, spacelift, token, tpm' type: string kubernetes: description: Kubernetes allows the configuration of options specific diff --git a/gen/proto/go/accessgraph/v1alpha/access_graph_service_grpc.pb.go b/gen/proto/go/accessgraph/v1alpha/access_graph_service_grpc.pb.go index 7f0812c5f881f..5f1ddcb26c27c 100644 --- a/gen/proto/go/accessgraph/v1alpha/access_graph_service_grpc.pb.go +++ b/gen/proto/go/accessgraph/v1alpha/access_graph_service_grpc.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: accessgraph/v1alpha/access_graph_service.proto @@ -32,8 +32,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( AccessGraphService_Query_FullMethodName = "/accessgraph.v1alpha.AccessGraphService/Query" @@ -64,11 +64,11 @@ type AccessGraphServiceClient interface { // This stream is used to sync the access graph with the Teleport database state. // Once Teleport finishes syncing the current state, it sends a sync command // to the access graph service and resumes sending events. - EventsStream(ctx context.Context, opts ...grpc.CallOption) (AccessGraphService_EventsStreamClient, error) + EventsStream(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[EventsStreamRequest, EventsStreamResponse], error) // EventsStreamV2 is a stream of commands to the access graph service. // This stream works the same way as EventsStream, but it returns a stream of events // instead of a single response. - EventsStreamV2(ctx context.Context, opts ...grpc.CallOption) (AccessGraphService_EventsStreamV2Client, error) + EventsStreamV2(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[EventsStreamV2Request, EventsStreamV2Response], error) // Register submits a new tenant representing this Teleport cluster to the TAG service, // identified by its HostCA certificate. // The method is idempotent: it succeeds if the tenant has already registered and has the specific CA associated. @@ -87,11 +87,11 @@ type AccessGraphServiceClient interface { // Teleport Discovery Service creates a stream to the access graph service // and pushes all AWS resources and following events to it. // This stream is used to sync the access graph with the AWS database state. - AWSEventsStream(ctx context.Context, opts ...grpc.CallOption) (AccessGraphService_AWSEventsStreamClient, error) + AWSEventsStream(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[AWSEventsStreamRequest, AWSEventsStreamResponse], error) // GitlabEventsStream is a stream of commands to the Gitlab importer. - GitlabEventsStream(ctx context.Context, opts ...grpc.CallOption) (AccessGraphService_GitlabEventsStreamClient, error) + GitlabEventsStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[GitlabEventsStreamRequest, GitlabEventsStreamResponse], error) // EntraEventsStream is a stream of commands to the Entra ID SSO importer. - EntraEventsStream(ctx context.Context, opts ...grpc.CallOption) (AccessGraphService_EntraEventsStreamClient, error) + EntraEventsStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[EntraEventsStreamRequest, EntraEventsStreamResponse], error) } type accessGraphServiceClient struct { @@ -122,72 +122,31 @@ func (c *accessGraphServiceClient) GetFile(ctx context.Context, in *GetFileReque return out, nil } -func (c *accessGraphServiceClient) EventsStream(ctx context.Context, opts ...grpc.CallOption) (AccessGraphService_EventsStreamClient, error) { +func (c *accessGraphServiceClient) EventsStream(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[EventsStreamRequest, EventsStreamResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &AccessGraphService_ServiceDesc.Streams[0], AccessGraphService_EventsStream_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &accessGraphServiceEventsStreamClient{ClientStream: stream} + x := &grpc.GenericClientStream[EventsStreamRequest, EventsStreamResponse]{ClientStream: stream} return x, nil } -type AccessGraphService_EventsStreamClient interface { - Send(*EventsStreamRequest) error - CloseAndRecv() (*EventsStreamResponse, error) - grpc.ClientStream -} - -type accessGraphServiceEventsStreamClient struct { - grpc.ClientStream -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type AccessGraphService_EventsStreamClient = grpc.ClientStreamingClient[EventsStreamRequest, EventsStreamResponse] -func (x *accessGraphServiceEventsStreamClient) Send(m *EventsStreamRequest) error { - return x.ClientStream.SendMsg(m) -} - -func (x *accessGraphServiceEventsStreamClient) CloseAndRecv() (*EventsStreamResponse, error) { - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - m := new(EventsStreamResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func (c *accessGraphServiceClient) EventsStreamV2(ctx context.Context, opts ...grpc.CallOption) (AccessGraphService_EventsStreamV2Client, error) { +func (c *accessGraphServiceClient) EventsStreamV2(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[EventsStreamV2Request, EventsStreamV2Response], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &AccessGraphService_ServiceDesc.Streams[1], AccessGraphService_EventsStreamV2_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &accessGraphServiceEventsStreamV2Client{ClientStream: stream} + x := &grpc.GenericClientStream[EventsStreamV2Request, EventsStreamV2Response]{ClientStream: stream} return x, nil } -type AccessGraphService_EventsStreamV2Client interface { - Send(*EventsStreamV2Request) error - Recv() (*EventsStreamV2Response, error) - grpc.ClientStream -} - -type accessGraphServiceEventsStreamV2Client struct { - grpc.ClientStream -} - -func (x *accessGraphServiceEventsStreamV2Client) Send(m *EventsStreamV2Request) error { - return x.ClientStream.SendMsg(m) -} - -func (x *accessGraphServiceEventsStreamV2Client) Recv() (*EventsStreamV2Response, error) { - m := new(EventsStreamV2Response) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type AccessGraphService_EventsStreamV2Client = grpc.BidiStreamingClient[EventsStreamV2Request, EventsStreamV2Response] func (c *accessGraphServiceClient) Register(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (*RegisterResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) @@ -209,108 +168,48 @@ func (c *accessGraphServiceClient) ReplaceCAs(ctx context.Context, in *ReplaceCA return out, nil } -func (c *accessGraphServiceClient) AWSEventsStream(ctx context.Context, opts ...grpc.CallOption) (AccessGraphService_AWSEventsStreamClient, error) { +func (c *accessGraphServiceClient) AWSEventsStream(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[AWSEventsStreamRequest, AWSEventsStreamResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &AccessGraphService_ServiceDesc.Streams[2], AccessGraphService_AWSEventsStream_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &accessGraphServiceAWSEventsStreamClient{ClientStream: stream} + x := &grpc.GenericClientStream[AWSEventsStreamRequest, AWSEventsStreamResponse]{ClientStream: stream} return x, nil } -type AccessGraphService_AWSEventsStreamClient interface { - Send(*AWSEventsStreamRequest) error - CloseAndRecv() (*AWSEventsStreamResponse, error) - grpc.ClientStream -} - -type accessGraphServiceAWSEventsStreamClient struct { - grpc.ClientStream -} - -func (x *accessGraphServiceAWSEventsStreamClient) Send(m *AWSEventsStreamRequest) error { - return x.ClientStream.SendMsg(m) -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type AccessGraphService_AWSEventsStreamClient = grpc.ClientStreamingClient[AWSEventsStreamRequest, AWSEventsStreamResponse] -func (x *accessGraphServiceAWSEventsStreamClient) CloseAndRecv() (*AWSEventsStreamResponse, error) { - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - m := new(AWSEventsStreamResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func (c *accessGraphServiceClient) GitlabEventsStream(ctx context.Context, opts ...grpc.CallOption) (AccessGraphService_GitlabEventsStreamClient, error) { +func (c *accessGraphServiceClient) GitlabEventsStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[GitlabEventsStreamRequest, GitlabEventsStreamResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &AccessGraphService_ServiceDesc.Streams[3], AccessGraphService_GitlabEventsStream_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &accessGraphServiceGitlabEventsStreamClient{ClientStream: stream} + x := &grpc.GenericClientStream[GitlabEventsStreamRequest, GitlabEventsStreamResponse]{ClientStream: stream} return x, nil } -type AccessGraphService_GitlabEventsStreamClient interface { - Send(*GitlabEventsStreamRequest) error - Recv() (*GitlabEventsStreamResponse, error) - grpc.ClientStream -} - -type accessGraphServiceGitlabEventsStreamClient struct { - grpc.ClientStream -} - -func (x *accessGraphServiceGitlabEventsStreamClient) Send(m *GitlabEventsStreamRequest) error { - return x.ClientStream.SendMsg(m) -} - -func (x *accessGraphServiceGitlabEventsStreamClient) Recv() (*GitlabEventsStreamResponse, error) { - m := new(GitlabEventsStreamResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type AccessGraphService_GitlabEventsStreamClient = grpc.BidiStreamingClient[GitlabEventsStreamRequest, GitlabEventsStreamResponse] -func (c *accessGraphServiceClient) EntraEventsStream(ctx context.Context, opts ...grpc.CallOption) (AccessGraphService_EntraEventsStreamClient, error) { +func (c *accessGraphServiceClient) EntraEventsStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[EntraEventsStreamRequest, EntraEventsStreamResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &AccessGraphService_ServiceDesc.Streams[4], AccessGraphService_EntraEventsStream_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &accessGraphServiceEntraEventsStreamClient{ClientStream: stream} + x := &grpc.GenericClientStream[EntraEventsStreamRequest, EntraEventsStreamResponse]{ClientStream: stream} return x, nil } -type AccessGraphService_EntraEventsStreamClient interface { - Send(*EntraEventsStreamRequest) error - Recv() (*EntraEventsStreamResponse, error) - grpc.ClientStream -} - -type accessGraphServiceEntraEventsStreamClient struct { - grpc.ClientStream -} - -func (x *accessGraphServiceEntraEventsStreamClient) Send(m *EntraEventsStreamRequest) error { - return x.ClientStream.SendMsg(m) -} - -func (x *accessGraphServiceEntraEventsStreamClient) Recv() (*EntraEventsStreamResponse, error) { - m := new(EntraEventsStreamResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type AccessGraphService_EntraEventsStreamClient = grpc.BidiStreamingClient[EntraEventsStreamRequest, EntraEventsStreamResponse] // AccessGraphServiceServer is the server API for AccessGraphService service. // All implementations must embed UnimplementedAccessGraphServiceServer -// for forward compatibility +// for forward compatibility. // // AccessGraphService is a service for interacting the access graph service. type AccessGraphServiceServer interface { @@ -325,11 +224,11 @@ type AccessGraphServiceServer interface { // This stream is used to sync the access graph with the Teleport database state. // Once Teleport finishes syncing the current state, it sends a sync command // to the access graph service and resumes sending events. - EventsStream(AccessGraphService_EventsStreamServer) error + EventsStream(grpc.ClientStreamingServer[EventsStreamRequest, EventsStreamResponse]) error // EventsStreamV2 is a stream of commands to the access graph service. // This stream works the same way as EventsStream, but it returns a stream of events // instead of a single response. - EventsStreamV2(AccessGraphService_EventsStreamV2Server) error + EventsStreamV2(grpc.BidiStreamingServer[EventsStreamV2Request, EventsStreamV2Response]) error // Register submits a new tenant representing this Teleport cluster to the TAG service, // identified by its HostCA certificate. // The method is idempotent: it succeeds if the tenant has already registered and has the specific CA associated. @@ -348,17 +247,20 @@ type AccessGraphServiceServer interface { // Teleport Discovery Service creates a stream to the access graph service // and pushes all AWS resources and following events to it. // This stream is used to sync the access graph with the AWS database state. - AWSEventsStream(AccessGraphService_AWSEventsStreamServer) error + AWSEventsStream(grpc.ClientStreamingServer[AWSEventsStreamRequest, AWSEventsStreamResponse]) error // GitlabEventsStream is a stream of commands to the Gitlab importer. - GitlabEventsStream(AccessGraphService_GitlabEventsStreamServer) error + GitlabEventsStream(grpc.BidiStreamingServer[GitlabEventsStreamRequest, GitlabEventsStreamResponse]) error // EntraEventsStream is a stream of commands to the Entra ID SSO importer. - EntraEventsStream(AccessGraphService_EntraEventsStreamServer) error + EntraEventsStream(grpc.BidiStreamingServer[EntraEventsStreamRequest, EntraEventsStreamResponse]) error mustEmbedUnimplementedAccessGraphServiceServer() } -// UnimplementedAccessGraphServiceServer must be embedded to have forward compatible implementations. -type UnimplementedAccessGraphServiceServer struct { -} +// UnimplementedAccessGraphServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedAccessGraphServiceServer struct{} func (UnimplementedAccessGraphServiceServer) Query(context.Context, *QueryRequest) (*QueryResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Query not implemented") @@ -366,10 +268,10 @@ func (UnimplementedAccessGraphServiceServer) Query(context.Context, *QueryReques func (UnimplementedAccessGraphServiceServer) GetFile(context.Context, *GetFileRequest) (*GetFileResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetFile not implemented") } -func (UnimplementedAccessGraphServiceServer) EventsStream(AccessGraphService_EventsStreamServer) error { +func (UnimplementedAccessGraphServiceServer) EventsStream(grpc.ClientStreamingServer[EventsStreamRequest, EventsStreamResponse]) error { return status.Errorf(codes.Unimplemented, "method EventsStream not implemented") } -func (UnimplementedAccessGraphServiceServer) EventsStreamV2(AccessGraphService_EventsStreamV2Server) error { +func (UnimplementedAccessGraphServiceServer) EventsStreamV2(grpc.BidiStreamingServer[EventsStreamV2Request, EventsStreamV2Response]) error { return status.Errorf(codes.Unimplemented, "method EventsStreamV2 not implemented") } func (UnimplementedAccessGraphServiceServer) Register(context.Context, *RegisterRequest) (*RegisterResponse, error) { @@ -378,16 +280,17 @@ func (UnimplementedAccessGraphServiceServer) Register(context.Context, *Register func (UnimplementedAccessGraphServiceServer) ReplaceCAs(context.Context, *ReplaceCAsRequest) (*ReplaceCAsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ReplaceCAs not implemented") } -func (UnimplementedAccessGraphServiceServer) AWSEventsStream(AccessGraphService_AWSEventsStreamServer) error { +func (UnimplementedAccessGraphServiceServer) AWSEventsStream(grpc.ClientStreamingServer[AWSEventsStreamRequest, AWSEventsStreamResponse]) error { return status.Errorf(codes.Unimplemented, "method AWSEventsStream not implemented") } -func (UnimplementedAccessGraphServiceServer) GitlabEventsStream(AccessGraphService_GitlabEventsStreamServer) error { +func (UnimplementedAccessGraphServiceServer) GitlabEventsStream(grpc.BidiStreamingServer[GitlabEventsStreamRequest, GitlabEventsStreamResponse]) error { return status.Errorf(codes.Unimplemented, "method GitlabEventsStream not implemented") } -func (UnimplementedAccessGraphServiceServer) EntraEventsStream(AccessGraphService_EntraEventsStreamServer) error { +func (UnimplementedAccessGraphServiceServer) EntraEventsStream(grpc.BidiStreamingServer[EntraEventsStreamRequest, EntraEventsStreamResponse]) error { return status.Errorf(codes.Unimplemented, "method EntraEventsStream not implemented") } func (UnimplementedAccessGraphServiceServer) mustEmbedUnimplementedAccessGraphServiceServer() {} +func (UnimplementedAccessGraphServiceServer) testEmbeddedByValue() {} // UnsafeAccessGraphServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to AccessGraphServiceServer will @@ -397,6 +300,13 @@ type UnsafeAccessGraphServiceServer interface { } func RegisterAccessGraphServiceServer(s grpc.ServiceRegistrar, srv AccessGraphServiceServer) { + // If the following call pancis, it indicates UnimplementedAccessGraphServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&AccessGraphService_ServiceDesc, srv) } @@ -437,56 +347,18 @@ func _AccessGraphService_GetFile_Handler(srv interface{}, ctx context.Context, d } func _AccessGraphService_EventsStream_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(AccessGraphServiceServer).EventsStream(&accessGraphServiceEventsStreamServer{ServerStream: stream}) -} - -type AccessGraphService_EventsStreamServer interface { - SendAndClose(*EventsStreamResponse) error - Recv() (*EventsStreamRequest, error) - grpc.ServerStream -} - -type accessGraphServiceEventsStreamServer struct { - grpc.ServerStream -} - -func (x *accessGraphServiceEventsStreamServer) SendAndClose(m *EventsStreamResponse) error { - return x.ServerStream.SendMsg(m) + return srv.(AccessGraphServiceServer).EventsStream(&grpc.GenericServerStream[EventsStreamRequest, EventsStreamResponse]{ServerStream: stream}) } -func (x *accessGraphServiceEventsStreamServer) Recv() (*EventsStreamRequest, error) { - m := new(EventsStreamRequest) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type AccessGraphService_EventsStreamServer = grpc.ClientStreamingServer[EventsStreamRequest, EventsStreamResponse] func _AccessGraphService_EventsStreamV2_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(AccessGraphServiceServer).EventsStreamV2(&accessGraphServiceEventsStreamV2Server{ServerStream: stream}) + return srv.(AccessGraphServiceServer).EventsStreamV2(&grpc.GenericServerStream[EventsStreamV2Request, EventsStreamV2Response]{ServerStream: stream}) } -type AccessGraphService_EventsStreamV2Server interface { - Send(*EventsStreamV2Response) error - Recv() (*EventsStreamV2Request, error) - grpc.ServerStream -} - -type accessGraphServiceEventsStreamV2Server struct { - grpc.ServerStream -} - -func (x *accessGraphServiceEventsStreamV2Server) Send(m *EventsStreamV2Response) error { - return x.ServerStream.SendMsg(m) -} - -func (x *accessGraphServiceEventsStreamV2Server) Recv() (*EventsStreamV2Request, error) { - m := new(EventsStreamV2Request) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type AccessGraphService_EventsStreamV2Server = grpc.BidiStreamingServer[EventsStreamV2Request, EventsStreamV2Response] func _AccessGraphService_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(RegisterRequest) @@ -525,82 +397,25 @@ func _AccessGraphService_ReplaceCAs_Handler(srv interface{}, ctx context.Context } func _AccessGraphService_AWSEventsStream_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(AccessGraphServiceServer).AWSEventsStream(&accessGraphServiceAWSEventsStreamServer{ServerStream: stream}) -} - -type AccessGraphService_AWSEventsStreamServer interface { - SendAndClose(*AWSEventsStreamResponse) error - Recv() (*AWSEventsStreamRequest, error) - grpc.ServerStream -} - -type accessGraphServiceAWSEventsStreamServer struct { - grpc.ServerStream -} - -func (x *accessGraphServiceAWSEventsStreamServer) SendAndClose(m *AWSEventsStreamResponse) error { - return x.ServerStream.SendMsg(m) + return srv.(AccessGraphServiceServer).AWSEventsStream(&grpc.GenericServerStream[AWSEventsStreamRequest, AWSEventsStreamResponse]{ServerStream: stream}) } -func (x *accessGraphServiceAWSEventsStreamServer) Recv() (*AWSEventsStreamRequest, error) { - m := new(AWSEventsStreamRequest) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type AccessGraphService_AWSEventsStreamServer = grpc.ClientStreamingServer[AWSEventsStreamRequest, AWSEventsStreamResponse] func _AccessGraphService_GitlabEventsStream_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(AccessGraphServiceServer).GitlabEventsStream(&accessGraphServiceGitlabEventsStreamServer{ServerStream: stream}) -} - -type AccessGraphService_GitlabEventsStreamServer interface { - Send(*GitlabEventsStreamResponse) error - Recv() (*GitlabEventsStreamRequest, error) - grpc.ServerStream -} - -type accessGraphServiceGitlabEventsStreamServer struct { - grpc.ServerStream -} - -func (x *accessGraphServiceGitlabEventsStreamServer) Send(m *GitlabEventsStreamResponse) error { - return x.ServerStream.SendMsg(m) + return srv.(AccessGraphServiceServer).GitlabEventsStream(&grpc.GenericServerStream[GitlabEventsStreamRequest, GitlabEventsStreamResponse]{ServerStream: stream}) } -func (x *accessGraphServiceGitlabEventsStreamServer) Recv() (*GitlabEventsStreamRequest, error) { - m := new(GitlabEventsStreamRequest) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type AccessGraphService_GitlabEventsStreamServer = grpc.BidiStreamingServer[GitlabEventsStreamRequest, GitlabEventsStreamResponse] func _AccessGraphService_EntraEventsStream_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(AccessGraphServiceServer).EntraEventsStream(&accessGraphServiceEntraEventsStreamServer{ServerStream: stream}) -} - -type AccessGraphService_EntraEventsStreamServer interface { - Send(*EntraEventsStreamResponse) error - Recv() (*EntraEventsStreamRequest, error) - grpc.ServerStream -} - -type accessGraphServiceEntraEventsStreamServer struct { - grpc.ServerStream -} - -func (x *accessGraphServiceEntraEventsStreamServer) Send(m *EntraEventsStreamResponse) error { - return x.ServerStream.SendMsg(m) + return srv.(AccessGraphServiceServer).EntraEventsStream(&grpc.GenericServerStream[EntraEventsStreamRequest, EntraEventsStreamResponse]{ServerStream: stream}) } -func (x *accessGraphServiceEntraEventsStreamServer) Recv() (*EntraEventsStreamRequest, error) { - m := new(EntraEventsStreamRequest) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type AccessGraphService_EntraEventsStreamServer = grpc.BidiStreamingServer[EntraEventsStreamRequest, EntraEventsStreamResponse] // AccessGraphService_ServiceDesc is the grpc.ServiceDesc for AccessGraphService service. // It's only intended for direct use with grpc.RegisterService, diff --git a/gen/proto/go/teleport/lib/teleterm/v1/service_grpc.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/service_grpc.pb.go index f389a1704c7e7..1af4f8cd62df8 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/service_grpc.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/service_grpc.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/lib/teleterm/v1/service.proto @@ -32,8 +32,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( TerminalService_UpdateTshdEventsServerAddress_FullMethodName = "/teleport.lib.teleterm.v1.TerminalService/UpdateTshdEventsServerAddress" @@ -171,11 +171,11 @@ type TerminalServiceClient interface { // <- Send list of credentials (e.g. usernames) associated with device // -> Receive the index number associated with the selected credential in list // <- End - LoginPasswordless(ctx context.Context, opts ...grpc.CallOption) (TerminalService_LoginPasswordlessClient, error) + LoginPasswordless(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[LoginPasswordlessRequest, LoginPasswordlessResponse], error) // ClusterLogin logs out a user from cluster Logout(ctx context.Context, in *LogoutRequest, opts ...grpc.CallOption) (*EmptyResponse, error) // TransferFile sends a request to download/upload a file - TransferFile(ctx context.Context, in *FileTransferRequest, opts ...grpc.CallOption) (TerminalService_TransferFileClient, error) + TransferFile(ctx context.Context, in *FileTransferRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[FileTransferProgress], error) // ReportUsageEvent allows to send usage events that are then anonymized and forwarded to prehog ReportUsageEvent(ctx context.Context, in *ReportUsageEventRequest, opts ...grpc.CallOption) (*EmptyResponse, error) // UpdateHeadlessAuthenticationState updates a headless authentication resource's state. @@ -491,37 +491,18 @@ func (c *terminalServiceClient) Login(ctx context.Context, in *LoginRequest, opt return out, nil } -func (c *terminalServiceClient) LoginPasswordless(ctx context.Context, opts ...grpc.CallOption) (TerminalService_LoginPasswordlessClient, error) { +func (c *terminalServiceClient) LoginPasswordless(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[LoginPasswordlessRequest, LoginPasswordlessResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &TerminalService_ServiceDesc.Streams[0], TerminalService_LoginPasswordless_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &terminalServiceLoginPasswordlessClient{ClientStream: stream} + x := &grpc.GenericClientStream[LoginPasswordlessRequest, LoginPasswordlessResponse]{ClientStream: stream} return x, nil } -type TerminalService_LoginPasswordlessClient interface { - Send(*LoginPasswordlessRequest) error - Recv() (*LoginPasswordlessResponse, error) - grpc.ClientStream -} - -type terminalServiceLoginPasswordlessClient struct { - grpc.ClientStream -} - -func (x *terminalServiceLoginPasswordlessClient) Send(m *LoginPasswordlessRequest) error { - return x.ClientStream.SendMsg(m) -} - -func (x *terminalServiceLoginPasswordlessClient) Recv() (*LoginPasswordlessResponse, error) { - m := new(LoginPasswordlessResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type TerminalService_LoginPasswordlessClient = grpc.BidiStreamingClient[LoginPasswordlessRequest, LoginPasswordlessResponse] func (c *terminalServiceClient) Logout(ctx context.Context, in *LogoutRequest, opts ...grpc.CallOption) (*EmptyResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) @@ -533,13 +514,13 @@ func (c *terminalServiceClient) Logout(ctx context.Context, in *LogoutRequest, o return out, nil } -func (c *terminalServiceClient) TransferFile(ctx context.Context, in *FileTransferRequest, opts ...grpc.CallOption) (TerminalService_TransferFileClient, error) { +func (c *terminalServiceClient) TransferFile(ctx context.Context, in *FileTransferRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[FileTransferProgress], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &TerminalService_ServiceDesc.Streams[1], TerminalService_TransferFile_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &terminalServiceTransferFileClient{ClientStream: stream} + x := &grpc.GenericClientStream[FileTransferRequest, FileTransferProgress]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } @@ -549,22 +530,8 @@ func (c *terminalServiceClient) TransferFile(ctx context.Context, in *FileTransf return x, nil } -type TerminalService_TransferFileClient interface { - Recv() (*FileTransferProgress, error) - grpc.ClientStream -} - -type terminalServiceTransferFileClient struct { - grpc.ClientStream -} - -func (x *terminalServiceTransferFileClient) Recv() (*FileTransferProgress, error) { - m := new(FileTransferProgress) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type TerminalService_TransferFileClient = grpc.ServerStreamingClient[FileTransferProgress] func (c *terminalServiceClient) ReportUsageEvent(ctx context.Context, in *ReportUsageEventRequest, opts ...grpc.CallOption) (*EmptyResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) @@ -678,7 +645,7 @@ func (c *terminalServiceClient) AuthenticateWebDevice(ctx context.Context, in *A // TerminalServiceServer is the server API for TerminalService service. // All implementations must embed UnimplementedTerminalServiceServer -// for forward compatibility +// for forward compatibility. // // TerminalService is used by the Electron app to communicate with the tsh daemon. // @@ -768,11 +735,11 @@ type TerminalServiceServer interface { // <- Send list of credentials (e.g. usernames) associated with device // -> Receive the index number associated with the selected credential in list // <- End - LoginPasswordless(TerminalService_LoginPasswordlessServer) error + LoginPasswordless(grpc.BidiStreamingServer[LoginPasswordlessRequest, LoginPasswordlessResponse]) error // ClusterLogin logs out a user from cluster Logout(context.Context, *LogoutRequest) (*EmptyResponse, error) // TransferFile sends a request to download/upload a file - TransferFile(*FileTransferRequest, TerminalService_TransferFileServer) error + TransferFile(*FileTransferRequest, grpc.ServerStreamingServer[FileTransferProgress]) error // ReportUsageEvent allows to send usage events that are then anonymized and forwarded to prehog ReportUsageEvent(context.Context, *ReportUsageEventRequest) (*EmptyResponse, error) // UpdateHeadlessAuthenticationState updates a headless authentication resource's state. @@ -811,9 +778,12 @@ type TerminalServiceServer interface { mustEmbedUnimplementedTerminalServiceServer() } -// UnimplementedTerminalServiceServer must be embedded to have forward compatible implementations. -type UnimplementedTerminalServiceServer struct { -} +// UnimplementedTerminalServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedTerminalServiceServer struct{} func (UnimplementedTerminalServiceServer) UpdateTshdEventsServerAddress(context.Context, *UpdateTshdEventsServerAddressRequest) (*UpdateTshdEventsServerAddressResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateTshdEventsServerAddress not implemented") @@ -896,13 +866,13 @@ func (UnimplementedTerminalServiceServer) GetCluster(context.Context, *GetCluste func (UnimplementedTerminalServiceServer) Login(context.Context, *LoginRequest) (*EmptyResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Login not implemented") } -func (UnimplementedTerminalServiceServer) LoginPasswordless(TerminalService_LoginPasswordlessServer) error { +func (UnimplementedTerminalServiceServer) LoginPasswordless(grpc.BidiStreamingServer[LoginPasswordlessRequest, LoginPasswordlessResponse]) error { return status.Errorf(codes.Unimplemented, "method LoginPasswordless not implemented") } func (UnimplementedTerminalServiceServer) Logout(context.Context, *LogoutRequest) (*EmptyResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Logout not implemented") } -func (UnimplementedTerminalServiceServer) TransferFile(*FileTransferRequest, TerminalService_TransferFileServer) error { +func (UnimplementedTerminalServiceServer) TransferFile(*FileTransferRequest, grpc.ServerStreamingServer[FileTransferProgress]) error { return status.Errorf(codes.Unimplemented, "method TransferFile not implemented") } func (UnimplementedTerminalServiceServer) ReportUsageEvent(context.Context, *ReportUsageEventRequest) (*EmptyResponse, error) { @@ -939,6 +909,7 @@ func (UnimplementedTerminalServiceServer) AuthenticateWebDevice(context.Context, return nil, status.Errorf(codes.Unimplemented, "method AuthenticateWebDevice not implemented") } func (UnimplementedTerminalServiceServer) mustEmbedUnimplementedTerminalServiceServer() {} +func (UnimplementedTerminalServiceServer) testEmbeddedByValue() {} // UnsafeTerminalServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to TerminalServiceServer will @@ -948,6 +919,13 @@ type UnsafeTerminalServiceServer interface { } func RegisterTerminalServiceServer(s grpc.ServiceRegistrar, srv TerminalServiceServer) { + // If the following call pancis, it indicates UnimplementedTerminalServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&TerminalService_ServiceDesc, srv) } @@ -1438,30 +1416,11 @@ func _TerminalService_Login_Handler(srv interface{}, ctx context.Context, dec fu } func _TerminalService_LoginPasswordless_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(TerminalServiceServer).LoginPasswordless(&terminalServiceLoginPasswordlessServer{ServerStream: stream}) -} - -type TerminalService_LoginPasswordlessServer interface { - Send(*LoginPasswordlessResponse) error - Recv() (*LoginPasswordlessRequest, error) - grpc.ServerStream -} - -type terminalServiceLoginPasswordlessServer struct { - grpc.ServerStream -} - -func (x *terminalServiceLoginPasswordlessServer) Send(m *LoginPasswordlessResponse) error { - return x.ServerStream.SendMsg(m) + return srv.(TerminalServiceServer).LoginPasswordless(&grpc.GenericServerStream[LoginPasswordlessRequest, LoginPasswordlessResponse]{ServerStream: stream}) } -func (x *terminalServiceLoginPasswordlessServer) Recv() (*LoginPasswordlessRequest, error) { - m := new(LoginPasswordlessRequest) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type TerminalService_LoginPasswordlessServer = grpc.BidiStreamingServer[LoginPasswordlessRequest, LoginPasswordlessResponse] func _TerminalService_Logout_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(LogoutRequest) @@ -1486,21 +1445,11 @@ func _TerminalService_TransferFile_Handler(srv interface{}, stream grpc.ServerSt if err := stream.RecvMsg(m); err != nil { return err } - return srv.(TerminalServiceServer).TransferFile(m, &terminalServiceTransferFileServer{ServerStream: stream}) -} - -type TerminalService_TransferFileServer interface { - Send(*FileTransferProgress) error - grpc.ServerStream -} - -type terminalServiceTransferFileServer struct { - grpc.ServerStream + return srv.(TerminalServiceServer).TransferFile(m, &grpc.GenericServerStream[FileTransferRequest, FileTransferProgress]{ServerStream: stream}) } -func (x *terminalServiceTransferFileServer) Send(m *FileTransferProgress) error { - return x.ServerStream.SendMsg(m) -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type TerminalService_TransferFileServer = grpc.ServerStreamingServer[FileTransferProgress] func _TerminalService_ReportUsageEvent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ReportUsageEventRequest) diff --git a/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service_grpc.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service_grpc.pb.go index bd558541e9342..7dc9087c46d05 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service_grpc.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service_grpc.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/lib/teleterm/v1/tshd_events_service.proto @@ -32,8 +32,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( TshdEventsService_Relogin_FullMethodName = "/teleport.lib.teleterm.v1.TshdEventsService/Relogin" @@ -147,7 +147,7 @@ func (c *tshdEventsServiceClient) ReportUnexpectedVnetShutdown(ctx context.Conte // TshdEventsServiceServer is the server API for TshdEventsService service. // All implementations must embed UnimplementedTshdEventsServiceServer -// for forward compatibility +// for forward compatibility. // // TshdEventsService is served by the Electron app. The tsh daemon calls this service to notify the // app about actions that happen outside of the app itself. @@ -179,9 +179,12 @@ type TshdEventsServiceServer interface { mustEmbedUnimplementedTshdEventsServiceServer() } -// UnimplementedTshdEventsServiceServer must be embedded to have forward compatible implementations. -type UnimplementedTshdEventsServiceServer struct { -} +// UnimplementedTshdEventsServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedTshdEventsServiceServer struct{} func (UnimplementedTshdEventsServiceServer) Relogin(context.Context, *ReloginRequest) (*ReloginResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Relogin not implemented") @@ -202,6 +205,7 @@ func (UnimplementedTshdEventsServiceServer) ReportUnexpectedVnetShutdown(context return nil, status.Errorf(codes.Unimplemented, "method ReportUnexpectedVnetShutdown not implemented") } func (UnimplementedTshdEventsServiceServer) mustEmbedUnimplementedTshdEventsServiceServer() {} +func (UnimplementedTshdEventsServiceServer) testEmbeddedByValue() {} // UnsafeTshdEventsServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to TshdEventsServiceServer will @@ -211,6 +215,13 @@ type UnsafeTshdEventsServiceServer interface { } func RegisterTshdEventsServiceServer(s grpc.ServiceRegistrar, srv TshdEventsServiceServer) { + // If the following call pancis, it indicates UnimplementedTshdEventsServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&TshdEventsService_ServiceDesc, srv) } diff --git a/gen/proto/go/teleport/lib/teleterm/vnet/v1/vnet_service_grpc.pb.go b/gen/proto/go/teleport/lib/teleterm/vnet/v1/vnet_service_grpc.pb.go index 2987dd3c82811..a3b9e909eeaa2 100644 --- a/gen/proto/go/teleport/lib/teleterm/vnet/v1/vnet_service_grpc.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/vnet/v1/vnet_service_grpc.pb.go @@ -16,7 +16,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/lib/teleterm/vnet/v1/vnet_service.proto @@ -31,8 +31,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( VnetService_Start_FullMethodName = "/teleport.lib.teleterm.vnet.v1.VnetService/Start" @@ -116,7 +116,7 @@ func (c *vnetServiceClient) GetBackgroundItemStatus(ctx context.Context, in *Get // VnetServiceServer is the server API for VnetService service. // All implementations must embed UnimplementedVnetServiceServer -// for forward compatibility +// for forward compatibility. // // VnetService provides methods to manage a VNet instance. type VnetServiceServer interface { @@ -140,9 +140,12 @@ type VnetServiceServer interface { mustEmbedUnimplementedVnetServiceServer() } -// UnimplementedVnetServiceServer must be embedded to have forward compatible implementations. -type UnimplementedVnetServiceServer struct { -} +// UnimplementedVnetServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedVnetServiceServer struct{} func (UnimplementedVnetServiceServer) Start(context.Context, *StartRequest) (*StartResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Start not implemented") @@ -157,6 +160,7 @@ func (UnimplementedVnetServiceServer) GetBackgroundItemStatus(context.Context, * return nil, status.Errorf(codes.Unimplemented, "method GetBackgroundItemStatus not implemented") } func (UnimplementedVnetServiceServer) mustEmbedUnimplementedVnetServiceServer() {} +func (UnimplementedVnetServiceServer) testEmbeddedByValue() {} // UnsafeVnetServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to VnetServiceServer will @@ -166,6 +170,13 @@ type UnsafeVnetServiceServer interface { } func RegisterVnetServiceServer(s grpc.ServiceRegistrar, srv VnetServiceServer) { + // If the following call pancis, it indicates UnimplementedVnetServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&VnetService_ServiceDesc, srv) } diff --git a/go.mod b/go.mod index acb5c2976fb0b..db3c63d92fa03 100644 --- a/go.mod +++ b/go.mod @@ -206,7 +206,7 @@ require ( google.golang.org/api v0.187.0 google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 google.golang.org/grpc v1.65.0 - google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0 + google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.0 google.golang.org/protobuf v1.34.2 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c gopkg.in/dnaeon/go-vcr.v3 v3.2.0 diff --git a/go.sum b/go.sum index 0e0887041f210..c520cbf0a5d58 100644 --- a/go.sum +++ b/go.sum @@ -3121,8 +3121,8 @@ google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpX google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0 h1:9SxA29VM43MF5Z9dQu694wmY5t8E/Gxr7s+RSxiIDmc= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0/go.mod h1:yZOK5zhQMiALmuweVdIVoQPa6eIJyXn2B9g5dJDhqX4= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.0 h1:LbGOiI82o4xuOWAYyRCrb7qgrRsKvlLRLcFal5iDz5w= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.0/go.mod h1:BQzMLXlekia34Yc4jZRJW0Rs0bhTn5/jMYPN349sUPA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/integration/proxy/teleterm_test.go b/integration/proxy/teleterm_test.go index 8608111886aec..e6172b0e3e664 100644 --- a/integration/proxy/teleterm_test.go +++ b/integration/proxy/teleterm_test.go @@ -297,7 +297,7 @@ func testGatewayCertRenewal(ctx context.Context, t *testing.T, params gatewayCer } type mockTSHDEventsService struct { - *api.UnimplementedTshdEventsServiceServer + api.UnimplementedTshdEventsServiceServer t *testing.T tc *libclient.TeleportClient diff --git a/integrations/access/opsgenie/client.go b/integrations/access/opsgenie/client.go index d44a892029232..9591d26834002 100644 --- a/integrations/access/opsgenie/client.go +++ b/integrations/access/opsgenie/client.go @@ -29,6 +29,7 @@ import ( "github.com/aws/aws-sdk-go/aws/defaults" "github.com/go-resty/resty/v2" + "github.com/google/uuid" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" @@ -219,22 +220,28 @@ func (og Client) getResponders(reqData RequestData) []Responder { } responders := make([]Responder, 0, len(schedules)+len(teams)) for _, s := range schedules { - responders = append(responders, Responder{ - Type: ResponderTypeSchedule, - ID: s, - Name: s, - }) + responders = append(responders, createResponder(ResponderTypeSchedule, s)) } for _, t := range teams { - responders = append(responders, Responder{ - Type: ResponderTypeTeam, - ID: t, - Name: t, - }) + responders = append(responders, createResponder(ResponderTypeTeam, t)) } return responders } +// Check if the responder is a UUID. If it is, then it is an ID; otherwise, it is a name. +func createResponder(responderType string, value string) Responder { + if _, err := uuid.Parse(value); err == nil { + return Responder{ + Type: responderType, + ID: value, + } + } + return Responder{ + Type: responderType, + Name: value, + } +} + // PostReviewNote posts a note once a new request review appears. func (og Client) PostReviewNote(ctx context.Context, alertID string, review types.AccessReview) error { note, err := buildReviewNoteBody(review) diff --git a/integrations/access/opsgenie/client_test.go b/integrations/access/opsgenie/client_test.go index e61e5004c0bcf..d03871731b14f 100644 --- a/integrations/access/opsgenie/client_test.go +++ b/integrations/access/opsgenie/client_test.go @@ -59,8 +59,8 @@ func TestCreateAlert(t *testing.T) { Roles: []string{"role1", "role2"}, RequestReason: "someReason", SystemAnnotations: types.Labels{ - types.TeleportNamespace + types.ReqAnnotationNotifySchedulesLabel: {"responder@example.com"}, - types.TeleportNamespace + types.ReqAnnotationTeamsLabel: {"MyOpsGenieTeam"}, + types.TeleportNamespace + types.ReqAnnotationNotifySchedulesLabel: {"responder@example.com", "bb4d9938-c3c2-455d-aaab-727aa701c0d8"}, + types.TeleportNamespace + types.ReqAnnotationTeamsLabel: {"MyOpsGenieTeam", "aee8a0de-c80f-4515-a232-501c0bc9d715"}, }, }) assert.NoError(t, err) @@ -70,8 +70,10 @@ func TestCreateAlert(t *testing.T) { Alias: "teleport-access-request/someRequestID", Description: "someUser requested permissions for roles role1, role2 on Teleport at 01 Jan 01 00:00 UTC.\nReason: someReason\n\n", Responders: []Responder{ - {Type: "schedule", Name: "responder@example.com", ID: "responder@example.com"}, - {Type: "team", Name: "MyOpsGenieTeam", ID: "MyOpsGenieTeam"}, + {Type: "schedule", Name: "responder@example.com"}, + {Type: "schedule", ID: "bb4d9938-c3c2-455d-aaab-727aa701c0d8"}, + {Type: "team", Name: "MyOpsGenieTeam"}, + {Type: "team", ID: "aee8a0de-c80f-4515-a232-501c0bc9d715"}, }, Priority: "somePriority", } diff --git a/integrations/event-handler/go.mod b/integrations/event-handler/go.mod index 11fd3836b122a..de23220d86396 100644 --- a/integrations/event-handler/go.mod +++ b/integrations/event-handler/go.mod @@ -288,7 +288,7 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/grpc v1.65.0 // indirect - google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0 // indirect + google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect diff --git a/integrations/event-handler/go.sum b/integrations/event-handler/go.sum index 31b54526320ec..2cc5589fab8c0 100644 --- a/integrations/event-handler/go.sum +++ b/integrations/event-handler/go.sum @@ -2339,8 +2339,8 @@ google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpX google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0 h1:9SxA29VM43MF5Z9dQu694wmY5t8E/Gxr7s+RSxiIDmc= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0/go.mod h1:yZOK5zhQMiALmuweVdIVoQPa6eIJyXn2B9g5dJDhqX4= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.0 h1:LbGOiI82o4xuOWAYyRCrb7qgrRsKvlLRLcFal5iDz5w= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.0/go.mod h1:BQzMLXlekia34Yc4jZRJW0Rs0bhTn5/jMYPN349sUPA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/integrations/lib/embeddedtbot/bot.go b/integrations/lib/embeddedtbot/bot.go index 9573ab52c99e0..e693b40793fe5 100644 --- a/integrations/lib/embeddedtbot/bot.go +++ b/integrations/lib/embeddedtbot/bot.go @@ -136,7 +136,7 @@ func (b *EmbeddedBot) Start(ctx context.Context) error { } } -func (b *EmbeddedBot) waitForClient(ctx context.Context, deadline time.Duration) (*client.Client, error) { +func (b *EmbeddedBot) waitForCredentials(ctx context.Context, deadline time.Duration) (client.Credentials, error) { waitCtx, cancel := context.WithTimeout(ctx, deadline) defer cancel() @@ -148,20 +148,32 @@ func (b *EmbeddedBot) waitForClient(ctx context.Context, deadline time.Duration) log.Infof("credential ready") } - c, err := b.buildClient(ctx) - return c, trace.Wrap(err) - + return b.credential, nil } // StartAndWaitForClient starts the EmbeddedBot and waits for a client to be available. -// This is the proper way of starting the EmbeddedBot. It returns an error if the -// EmbeddedBot is not able to get a certificate before the deadline. +// It returns an error if the EmbeddedBot is not able to get a certificate before the deadline. +// If you need a client.Credentials instead, you can use StartAndWaitForCredentials. func (b *EmbeddedBot) StartAndWaitForClient(ctx context.Context, deadline time.Duration) (*client.Client, error) { b.start(ctx) - c, err := b.waitForClient(ctx, deadline) + _, err := b.waitForCredentials(ctx, deadline) + if err != nil { + return nil, trace.Wrap(err) + } + + c, err := b.buildClient(ctx) return c, trace.Wrap(err) } +// StartAndWaitForCredentials starts the EmbeddedBot and waits for credentials to become ready. +// It returns an error if the EmbeddedBot is not able to get a certificate before the deadline. +// If you need a client.Client instead, you can use StartAndWaitForClient. +func (b *EmbeddedBot) StartAndWaitForCredentials(ctx context.Context, deadline time.Duration) (client.Credentials, error) { + b.start(ctx) + creds, err := b.waitForCredentials(ctx, deadline) + return creds, trace.Wrap(err) +} + // buildClient reads tbot's memory disttination, retrieves the certificates // and builds a new Teleport client using those certs. func (b *EmbeddedBot) buildClient(ctx context.Context) (*client.Client, error) { diff --git a/integrations/lib/embeddedtbot/bot_test.go b/integrations/lib/embeddedtbot/bot_test.go index 18075e81cf945..ca4bc9480af43 100644 --- a/integrations/lib/embeddedtbot/bot_test.go +++ b/integrations/lib/embeddedtbot/bot_test.go @@ -134,7 +134,7 @@ func TestBotJoinAuth(t *testing.T) { require.NoError(t, err) require.Equal(t, clusterName, pong.ClusterName) - botClient, err := bot.waitForClient(ctx, 10*time.Second) + botClient, err := bot.StartAndWaitForClient(ctx, 10*time.Second) require.NoError(t, err) botPong, err := botClient.Ping(ctx) require.NoError(t, err) diff --git a/integrations/operator/config/crd/bases/resources.teleport.dev_provisiontokens.yaml b/integrations/operator/config/crd/bases/resources.teleport.dev_provisiontokens.yaml index edd501d22255c..1f733e28928ee 100644 --- a/integrations/operator/config/crd/bases/resources.teleport.dev_provisiontokens.yaml +++ b/integrations/operator/config/crd/bases/resources.teleport.dev_provisiontokens.yaml @@ -257,9 +257,9 @@ spec: type: string type: object join_method: - description: JoinMethod is the joining method required in order to - use this token. Supported joining methods include "token", "ec2", - and "iam". + description: 'JoinMethod is the joining method required in order to + use this token. Supported joining methods include: azure, circleci, + ec2, gcp, github, gitlab, iam, kubernetes, spacelift, token, tpm' type: string kubernetes: description: Kubernetes allows the configuration of options specific diff --git a/integrations/terraform/go.mod b/integrations/terraform/go.mod index 9647eeb1bf5f7..a1c00dbbe59e2 100644 --- a/integrations/terraform/go.mod +++ b/integrations/terraform/go.mod @@ -28,6 +28,8 @@ require ( google.golang.org/protobuf v1.34.2 ) +require github.com/hashicorp/terraform-plugin-log v0.9.0 + require ( cloud.google.com/go v0.115.0 // indirect cloud.google.com/go/auth v0.6.1 // indirect @@ -207,7 +209,6 @@ require ( github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/terraform-exec v0.21.0 // indirect github.com/hashicorp/terraform-json v0.22.1 // indirect - github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect github.com/hashicorp/terraform-registry-address v0.2.1 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // indirect github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect @@ -220,6 +221,7 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmoiron/sqlx v1.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect + github.com/joshlf/go-acl v0.0.0-20200411065538-eae00ae38531 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/julienschmidt/httprouter v1.3.0 // indirect github.com/kelseyhightower/envconfig v1.4.0 // indirect @@ -290,6 +292,7 @@ require ( github.com/spf13/cast v1.6.0 // indirect github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/spiffe/go-spiffe/v2 v2.3.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/thales-e-security/pool v0.0.2 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect @@ -338,7 +341,7 @@ require ( google.golang.org/genproto v0.0.0-20240624140628-dc46fd24d27d // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect - google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0 // indirect + google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect diff --git a/integrations/terraform/go.sum b/integrations/terraform/go.sum index 828f1da4ec0ca..9928b0f40cfb0 100644 --- a/integrations/terraform/go.sum +++ b/integrations/terraform/go.sum @@ -1075,6 +1075,8 @@ github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= +github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk= +github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= @@ -1514,6 +1516,10 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= +github.com/joshlf/go-acl v0.0.0-20200411065538-eae00ae38531 h1:hgVxRoDDPtQE68PT4LFvNlPz2nBKd3OMlGKIQ69OmR4= +github.com/joshlf/go-acl v0.0.0-20200411065538-eae00ae38531/go.mod h1:fqTUQpVYBvhCNIsMXGl2GE9q6z94DIP6NtFKXCSTVbg= +github.com/joshlf/testutil v0.0.0-20170608050642-b5d8aa79d93d h1:J8tJzRyiddAFF65YVgxli+TyWBi0f79Sld6rJP6CBcY= +github.com/joshlf/testutil v0.0.0-20170608050642-b5d8aa79d93d/go.mod h1:b+Q3v8Yrg5o15d71PSUraUzYb+jWl6wQMSBXSGS/hv0= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -1839,6 +1845,8 @@ github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3k github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spiffe/go-spiffe/v2 v2.3.0 h1:g2jYNb/PDMB8I7mBGL2Zuq/Ur6hUhoroxGQFyD6tTj8= +github.com/spiffe/go-spiffe/v2 v2.3.0/go.mod h1:Oxsaio7DBgSNqhAO9i/9tLClaVlfRok7zvJnTV8ZyIY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -1928,6 +1936,8 @@ github.com/zclconf/go-cty v1.14.4 h1:uXXczd9QDGsgu0i/QFR/hzI5NYCHLf6NQw/atrbnhq8 github.com/zclconf/go-cty v1.14.4/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs= +github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= @@ -2443,6 +2453,8 @@ golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNq golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= @@ -2705,8 +2717,8 @@ google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpX google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0 h1:9SxA29VM43MF5Z9dQu694wmY5t8E/Gxr7s+RSxiIDmc= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0/go.mod h1:yZOK5zhQMiALmuweVdIVoQPa6eIJyXn2B9g5dJDhqX4= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.0 h1:LbGOiI82o4xuOWAYyRCrb7qgrRsKvlLRLcFal5iDz5w= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.0/go.mod h1:BQzMLXlekia34Yc4jZRJW0Rs0bhTn5/jMYPN349sUPA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/integrations/terraform/provider/credentials.go b/integrations/terraform/provider/credentials.go new file mode 100644 index 0000000000000..0e6a26804ad65 --- /dev/null +++ b/integrations/terraform/provider/credentials.go @@ -0,0 +1,570 @@ +/* +Copyright 2024 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provider + +import ( + "bytes" + "context" + "encoding/base64" + "fmt" + "strings" + "text/template" + "time" + + "github.com/gravitational/trace" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-log/tflog" + + "github.com/gravitational/teleport/api/client" + "github.com/gravitational/teleport/api/constants" + apitypes "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/integrations/lib/embeddedtbot" + tbotconfig "github.com/gravitational/teleport/lib/tbot/config" +) + +var supportedCredentialSources = CredentialSources{ + CredentialsFromNativeMachineID{}, + CredentialsFromKeyAndCertPath{}, + CredentialsFromKeyAndCertBase64{}, + CredentialsFromIdentityFilePath{}, + CredentialsFromIdentityFileString{}, + CredentialsFromIdentityFileBase64{}, + CredentialsFromProfile{}, +} + +// CredentialSources is a list of CredentialSource +type CredentialSources []CredentialSource + +// ActiveSources returns the list of active sources, and an error diagnostic if no source is active. +// The error diagnostic explains why every source is inactive. +func (s CredentialSources) ActiveSources(ctx context.Context, config providerData) (CredentialSources, diag.Diagnostics) { + var activeSources CredentialSources + inactiveReason := strings.Builder{} + for _, source := range s { + active, reason := source.IsActive(config) + logFields := map[string]interface{}{ + "source": source.Name(), + "active": active, + "reason": reason, + } + if !active { + tflog.Info(ctx, "credentials source is not active, skipping", logFields) + inactiveReason.WriteString(fmt.Sprintf(" - cannot read credentials %s because %s\n", source.Name(), reason)) + continue + } + tflog.Info(ctx, "credentials source is active", logFields) + activeSources = append(activeSources, source) + } + if len(activeSources) == 0 { + // TODO: make this a hard failure in v17 + // We currently try to load credentials from the user profile. + // As trying broken credentials takes 30 seconds this is a very bad UX and we should get rid of this. + // Credentials from profile are not passing MFA4Admin anyway. + summary := inactiveReason.String() + + "\nThe provider will fallback to your current local profile (this behavior is deprecated and will be removed in v17, you should specify the profile name or directory)." + return CredentialSources{CredentialsFromProfile{isDefault: true}}, diag.Diagnostics{diag.NewWarningDiagnostic( + "No active Teleport credentials source found", + summary, + )} + } + return activeSources, nil +} + +// BuildClient sequentially builds credentials for every source and tries to use them to connect to Teleport. +// Any CredentialSource failing to return a Credential and a tls.Config causes a hard failure. +// If we have a valid credential but cannot connect, we send a warning and continue with the next credential +// (this is for backward compatibility). +// Expired credentials are skipped for the sake of UX. This is the most common failure mode and we can +// return an error quickly instead of hanging for 30 whole seconds. +func (s CredentialSources) BuildClient(ctx context.Context, clientCfg client.Config, providerCfg providerData) (*client.Client, diag.Diagnostics) { + diags := diag.Diagnostics{} + for _, source := range s { + logFields := map[string]interface{}{ + "source": source.Name(), + } + tflog.Info(ctx, fmt.Sprintf("trying to build a client %s", source.Name()), logFields) + creds, err := source.Credentials(ctx, providerCfg) + if err != nil { + logFields["error"] = err.Error() + tflog.Error(ctx, "failed to obtain credential", logFields) + _, reason := source.IsActive(providerCfg) + diags.AddError( + fmt.Sprintf("Failed to obtain Teleport credentials %s", source.Name()), + brokenCredentialErrorSummary(source.Name(), reason, err), + ) + return nil, diags + } + + // Smoke test to see if the credential is valid + // This catches all the "file not found" issues and other broken credentials + // so we can turn them into a hard failure. + _, err = creds.TLSConfig() + if err != nil { + logFields["error"] = err.Error() + tflog.Error(ctx, "failed to get a TLSConfig from the credential", logFields) + _, reason := source.IsActive(providerCfg) + diags.AddError( + fmt.Sprintf("Invalid Teleport credentials %s", source.Name()), + brokenCredentialErrorSummary(source.Name(), reason, err), + ) + + return nil, diags + } + + now := time.Now() + if expiry, ok := creds.Expiry(); ok && !expiry.IsZero() && expiry.Before(now) { + diags.AddWarning( + fmt.Sprintf("Teleport credentials %s are expired", source.Name()), + fmt.Sprintf(`The credentials %s are expired. Expiration is %q while current time is %q). You might need to refresh them. The provider will not attempt to use those credentials.`, + source.Name(), expiry.Local(), now.Local()), + ) + continue + } + + clientCfg.Credentials = []client.Credentials{creds} + // In case of connection failure, this takes 30 seconds to return, which is very, very long. + clt, err := client.New(ctx, clientCfg) + if err != nil { + logFields["error"] = err.Error() + tflog.Error(ctx, "failed to connect with the credential", logFields) + diags.AddWarning( + fmt.Sprintf("Failed to connect with credentials %s", source.Name()), + fmt.Sprintf("The client built from the credentials %s failed to connect to %q with the error: %s.", + source.Name(), clientCfg.Addrs[0], err, + )) + continue + } + // A client was successfully built + return clt, diags + } + // No client was built + diags.AddError("Impossible to build Teleport client", s.failedToBuildClientErrorSummary(clientCfg.Addrs[0])) + return nil, diags +} + +const failedToBuildClientErrorTemplate = `"Every credential source provided has failed. The Terraform provider cannot connect to the Teleport cluster '{{.Addr}}'. + +The provider tried building a client: +{{- range $_, $source := .Sources }} +- {{ $source }} +{{- end }} + +You can find more information about why each credential source failed in the Terraform warnings above this error.` + +// failedToBuildClientErrorSummary builds a user-friendly message explaining we failed to build a functional Teleport +// client and listing every connection method we tried. +func (s CredentialSources) failedToBuildClientErrorSummary(addr string) string { + var sources []string + for _, source := range s { + sources = append(sources, source.Name()) + } + + tpl := template.Must(template.New("failed-to-build-client-error-summary").Parse(failedToBuildClientErrorTemplate)) + values := struct { + Addr string + Sources []string + }{ + Addr: addr, + Sources: sources, + } + buffer := new(bytes.Buffer) + err := tpl.Execute(buffer, values) + if err != nil { + return "Failed to build error summary. This is a provider bug: " + err.Error() + } + return buffer.String() +} + +const brokenCredentialErrorTemplate = `The Terraform provider tried to build credentials {{ .Source }} but received the following error: + +{{ .Error }} + +The provider tried to use the credential source because {{ .Reason }}. You must either address the error or disable the credential source by removing its values.` + +// brokenCredentialErrorSummary returns a user-friendly message explaining why we failed to +func brokenCredentialErrorSummary(name, activeReason string, err error) string { + tpl := template.Must(template.New("broken-credential-error-summary").Parse(brokenCredentialErrorTemplate)) + values := struct { + Source string + Error string + Reason string + }{ + Source: name, + Error: err.Error(), + Reason: activeReason, + } + buffer := new(bytes.Buffer) + tplErr := tpl.Execute(buffer, values) + if tplErr != nil { + return fmt.Sprintf("Failed to build error '%s' summary. This is a provider bug: %s", err, tplErr) + } + return buffer.String() +} + +// CredentialSource is a potential way for the Terraform provider to obtain the +// client.Credentials needed to connect to the Teleport cluster. +// A CredentialSource is active if the user specified configuration specific to this source. +// Only active CredentialSources are considered by the Provider. +type CredentialSource interface { + Name() string + IsActive(providerData) (bool, string) + Credentials(context.Context, providerData) (client.Credentials, error) +} + +// CredentialsFromKeyAndCertPath builds credentials from key, cert and ca cert paths. +type CredentialsFromKeyAndCertPath struct{} + +// Name implements CredentialSource and returns the source name. +func (CredentialsFromKeyAndCertPath) Name() string { + return "from Key, Cert, and CA path" +} + +// IsActive implements CredentialSource and returns if the source is active and why. +func (CredentialsFromKeyAndCertPath) IsActive(config providerData) (bool, string) { + certPath := stringFromConfigOrEnv(config.CertPath, constants.EnvVarTerraformCertificates, "") + keyPath := stringFromConfigOrEnv(config.KeyPath, constants.EnvVarTerraformKey, "") + + // This method is active as soon as a cert or a key path are set. + active := certPath != "" || keyPath != "" + + return activeReason( + active, + attributeTerraformCertificates, attributeTerraformKey, + constants.EnvVarTerraformCertificates, constants.EnvVarTerraformKey, + ) +} + +// Credentials implements CredentialSource and returns a client.Credentials for the provider. +func (CredentialsFromKeyAndCertPath) Credentials(ctx context.Context, config providerData) (client.Credentials, error) { + certPath := stringFromConfigOrEnv(config.CertPath, constants.EnvVarTerraformCertificates, "") + keyPath := stringFromConfigOrEnv(config.KeyPath, constants.EnvVarTerraformKey, "") + caPath := stringFromConfigOrEnv(config.RootCaPath, constants.EnvVarTerraformRootCertificates, "") + + // Validate that we have all paths. + if certPath == "" { + return nil, trace.BadParameter("missing parameter %q or environment variable %q", attributeTerraformCertificates, constants.EnvVarTerraformCertificates) + } + if keyPath == "" { + return nil, trace.BadParameter("missing parameter %q or environment variable %q", attributeTerraformKey, constants.EnvVarTerraformKey) + } + if caPath == "" { + return nil, trace.BadParameter("missing parameter %q or environment variable %q", attributeTerraformRootCertificates, constants.EnvVarTerraformRootCertificates) + } + + // Validate the files exist for a better UX? + + creds := client.LoadKeyPair(certPath, keyPath, caPath) + return creds, nil +} + +// CredentialsFromKeyAndCertBase64 builds credentials from key, cert, and CA cert base64. +type CredentialsFromKeyAndCertBase64 struct{} + +// Name implements CredentialSource and returns the source name. +func (CredentialsFromKeyAndCertBase64) Name() string { + return "from Key, Cert, and CA base64" +} + +// IsActive implements CredentialSource and returns if the source is active and why. +func (CredentialsFromKeyAndCertBase64) IsActive(config providerData) (bool, string) { + certBase64 := stringFromConfigOrEnv(config.CertBase64, constants.EnvVarTerraformCertificatesBase64, "") + keyBase64 := stringFromConfigOrEnv(config.KeyBase64, constants.EnvVarTerraformKeyBase64, "") + + // This method is active as soon as a cert or a key is passed. + active := certBase64 != "" || keyBase64 != "" + + return activeReason( + active, + attributeTerraformCertificatesBase64, attributeTerraformKeyBase64, + constants.EnvVarTerraformCertificatesBase64, constants.EnvVarTerraformKeyBase64, + ) +} + +// Credentials implements CredentialSource and returns a client.Credentials for the provider. +func (CredentialsFromKeyAndCertBase64) Credentials(ctx context.Context, config providerData) (client.Credentials, error) { + certBase64 := stringFromConfigOrEnv(config.CertBase64, constants.EnvVarTerraformCertificatesBase64, "") + keyBase64 := stringFromConfigOrEnv(config.KeyBase64, constants.EnvVarTerraformKeyBase64, "") + caBase64 := stringFromConfigOrEnv(config.RootCaBase64, constants.EnvVarTerraformRootCertificatesBase64, "") + + // Validate that we have all paths. + if certBase64 == "" { + return nil, trace.BadParameter("missing parameter %q or environment variable %q", attributeTerraformCertificatesBase64, constants.EnvVarTerraformCertificatesBase64) + } + if keyBase64 == "" { + return nil, trace.BadParameter("missing parameter %q or environment variable %q", attributeTerraformKeyBase64, constants.EnvVarTerraformKeyBase64) + } + if caBase64 == "" { + return nil, trace.BadParameter("missing parameter %q or environment variable %q", attributeTerraformRootCertificatesBase64, constants.EnvVarTerraformRootCertificatesBase64) + } + + certPEM, err := base64.StdEncoding.DecodeString(certBase64) + if err != nil { + return nil, trace.Wrap(err, "failed to decode the certificate's base64 (standard b64 encoding)") + } + keyPEM, err := base64.StdEncoding.DecodeString(keyBase64) + if err != nil { + return nil, trace.Wrap(err, "failed to decode the key's base64 (standard b64 encoding)") + } + caPEM, err := base64.StdEncoding.DecodeString(caBase64) + if err != nil { + return nil, trace.Wrap(err, "failed to decode the CA's base64 (standard b64 encoding)") + } + + creds, err := client.KeyPair(certPEM, keyPEM, caPEM) + return creds, trace.Wrap(err, "failed to load credentials from the PEM-encoded key and certificate") +} + +// CredentialsFromIdentityFilePath builds credentials from an identity file path. +type CredentialsFromIdentityFilePath struct{} + +// Name implements CredentialSource and returns the source name. +func (CredentialsFromIdentityFilePath) Name() string { + return "from the identity file path" +} + +// IsActive implements CredentialSource and returns if the source is active and why. +func (CredentialsFromIdentityFilePath) IsActive(config providerData) (bool, string) { + identityFilePath := stringFromConfigOrEnv(config.IdentityFilePath, constants.EnvVarTerraformIdentityFilePath, "") + + active := identityFilePath != "" + + return activeReason( + active, + attributeTerraformIdentityFilePath, constants.EnvVarTerraformIdentityFilePath, + ) +} + +// Credentials implements CredentialSource and returns a client.Credentials for the provider. +func (CredentialsFromIdentityFilePath) Credentials(ctx context.Context, config providerData) (client.Credentials, error) { + identityFilePath := stringFromConfigOrEnv(config.IdentityFilePath, constants.EnvVarTerraformIdentityFilePath, "") + + return client.LoadIdentityFile(identityFilePath), nil +} + +// CredentialsFromIdentityFileString builds credentials from an identity file passed as a string. +type CredentialsFromIdentityFileString struct{} + +// Name implements CredentialSource and returns the source name. +func (CredentialsFromIdentityFileString) Name() string { + return "from the identity file (passed as a string)" +} + +// IsActive implements CredentialSource and returns if the source is active and why. +func (CredentialsFromIdentityFileString) IsActive(config providerData) (bool, string) { + identityFileString := stringFromConfigOrEnv(config.IdentityFile, constants.EnvVarTerraformIdentityFile, "") + + active := identityFileString != "" + + return activeReason( + active, + attributeTerraformIdentityFile, constants.EnvVarTerraformIdentityFile, + ) +} + +// Credentials implements CredentialSource and returns a client.Credentials for the provider. +func (CredentialsFromIdentityFileString) Credentials(ctx context.Context, config providerData) (client.Credentials, error) { + identityFileString := stringFromConfigOrEnv(config.IdentityFile, constants.EnvVarTerraformIdentityFile, "") + + return client.LoadIdentityFileFromString(identityFileString), nil +} + +// CredentialsFromIdentityFileBase64 builds credentials from an identity file passed as a base64-encoded string. +type CredentialsFromIdentityFileBase64 struct{} + +// Name implements CredentialSource and returns the source name. +func (CredentialsFromIdentityFileBase64) Name() string { + return "from the identity file (passed as a base64-encoded string)" +} + +// IsActive implements CredentialSource and returns if the source is active and why. +func (CredentialsFromIdentityFileBase64) IsActive(config providerData) (bool, string) { + identityFileBase64 := stringFromConfigOrEnv(config.IdentityFileBase64, constants.EnvVarTerraformIdentityFileBase64, "") + + // This method is active as soon as a cert or a key path are set. + active := identityFileBase64 != "" + + return activeReason( + active, + attributeTerraformIdentityFileBase64, constants.EnvVarTerraformIdentityFileBase64, + ) +} + +// Credentials implements CredentialSource and returns a client.Credentials for the provider. +func (CredentialsFromIdentityFileBase64) Credentials(ctx context.Context, config providerData) (client.Credentials, error) { + identityFileBase64 := stringFromConfigOrEnv(config.IdentityFileBase64, constants.EnvVarTerraformIdentityFileBase64, "") + + identityFile, err := base64.StdEncoding.DecodeString(identityFileBase64) + if err != nil { + return nil, trace.Wrap(err, "decoding base64 identity file") + } + + return client.LoadIdentityFileFromString(string(identityFile)), nil +} + +// CredentialsFromProfile builds credentials from a local tsh profile. +type CredentialsFromProfile struct { + // isDefault represent if the CredentialSource is used as the default one. + // In this case, it explains that it is always active. + isDefault bool +} + +// Name implements CredentialSource and returns the source name. +func (c CredentialsFromProfile) Name() string { + name := "from the local profile" + if c.isDefault { + name += " (default)" + } + return name +} + +// IsActive implements CredentialSource and returns if the source is active and why. +func (c CredentialsFromProfile) IsActive(config providerData) (bool, string) { + if c.isDefault { + return true, "this is the default credential source, and no other credential was active" + } + + profileName := stringFromConfigOrEnv(config.ProfileName, constants.EnvVarTerraformProfileName, "") + profileDir := stringFromConfigOrEnv(config.ProfileDir, constants.EnvVarTerraformProfilePath, "") + + // This method is active as soon as a cert or a key path are set. + active := profileDir != "" || profileName != "" + return activeReason( + active, + attributeTerraformProfileName, attributeTerraformProfilePath, + constants.EnvVarTerraformProfileName, constants.EnvVarTerraformProfilePath, + ) +} + +// Credentials implements CredentialSource and returns a client.Credentials for the provider. +func (CredentialsFromProfile) Credentials(ctx context.Context, config providerData) (client.Credentials, error) { + profileName := stringFromConfigOrEnv(config.ProfileName, constants.EnvVarTerraformProfileName, "") + profileDir := stringFromConfigOrEnv(config.ProfileDir, constants.EnvVarTerraformProfilePath, "") + + return client.LoadProfile(profileDir, profileName), nil +} + +// CredentialsFromNativeMachineID builds credentials by performing a MachineID join and +type CredentialsFromNativeMachineID struct{} + +// Name implements CredentialSource and returns the source name. +func (CredentialsFromNativeMachineID) Name() string { + return "by performing native MachineID joining" +} + +// IsActive implements CredentialSource and returns if the source is active and why. +func (CredentialsFromNativeMachineID) IsActive(config providerData) (bool, string) { + joinMethod := stringFromConfigOrEnv(config.JoinMethod, constants.EnvVarTerraformJoinMethod, "") + joinToken := stringFromConfigOrEnv(config.JoinToken, constants.EnvVarTerraformJoinToken, "") + + // This method is active as soon as a token or a join method are set. + active := joinMethod != "" || joinToken != "" + return activeReason( + active, + attributeTerraformJoinMethod, attributeTerraformJoinToken, + constants.EnvVarTerraformJoinMethod, constants.EnvVarTerraformJoinToken, + ) +} + +// Credentials implements CredentialSource and returns a client.Credentials for the provider. +func (CredentialsFromNativeMachineID) Credentials(ctx context.Context, config providerData) (client.Credentials, error) { + joinMethod := stringFromConfigOrEnv(config.JoinMethod, constants.EnvVarTerraformJoinMethod, "") + joinToken := stringFromConfigOrEnv(config.JoinToken, constants.EnvVarTerraformJoinToken, "") + addr := stringFromConfigOrEnv(config.Addr, constants.EnvVarTerraformAddress, "") + caPath := stringFromConfigOrEnv(config.RootCaPath, constants.EnvVarTerraformRootCertificates, "") + + if joinMethod == "" { + return nil, trace.BadParameter("missing parameter %q or environment variable %q", attributeTerraformJoinMethod, constants.EnvVarTerraformJoinMethod) + } + if joinToken == "" { + return nil, trace.BadParameter("missing parameter %q or environment variable %q", attributeTerraformJoinMethod, constants.EnvVarTerraformJoinMethod) + } + if addr == "" { + return nil, trace.BadParameter("missing parameter %q or environment variable %q", attributeTerraformAddress, constants.EnvVarTerraformAddress) + } + + if apitypes.JoinMethod(joinMethod) == apitypes.JoinMethodToken { + return nil, trace.BadParameter(`the secret token join method ('token') is not supported for native Machine ID joining. + +Secret tokens are single use and the Terraform provider does not save the certificates it obtained, so the token join method can only be used once. +If you want to run the Terraform provider in the CI (GitHub Actions, GitlabCI, Circle CI) or in a supported runtime (AWS, GCP, Azure, Kubernetes, machine with a TPM) +you should use the join method specific to your environment. +If you want to use MachineID with secret tokens, the best approach is to run a local tbot on the server where the terraform provider runs. + +See https://goteleport.com/docs/reference/join-methods for more details.`) + } + + if err := apitypes.ValidateJoinMethod(apitypes.JoinMethod(joinMethod)); err != nil { + return nil, trace.Wrap(err, "Invalid Join Method") + } + botConfig := &embeddedtbot.BotConfig{ + AuthServer: addr, + Onboarding: tbotconfig.OnboardingConfig{ + TokenValue: joinToken, + CAPath: caPath, + JoinMethod: apitypes.JoinMethod(joinMethod), + }, + CertificateTTL: time.Hour, + RenewalInterval: 20 * time.Minute, + } + bot, err := embeddedtbot.New(botConfig) + if err != nil { + return nil, trace.Wrap(err, "Failed to create bot configuration, this is a provider bug, please open a GitHub issue.") + } + + preflightCtx, cancel := context.WithTimeout(ctx, 20*time.Second) + defer cancel() + _, err = bot.Preflight(preflightCtx) + if err != nil { + return nil, trace.Wrap(err, "Failed to preflight bot configuration") + } + + creds, err := bot.StartAndWaitForCredentials(ctx, 20*time.Second /* deadline */) + return creds, trace.Wrap(err, "Waiting for bot to obtain credentials") +} + +// activeReason renders a user-friendly active reason message describing if the credentials source is active +// and which parameters are controlling its activity. +func activeReason(active bool, params ...string) (bool, string) { + sb := new(strings.Builder) + var firstConjunction, lastConjunction string + + switch active { + case true: + firstConjunction = "either " + lastConjunction = "or " + case false: + firstConjunction = "neither " + lastConjunction = "nor " + } + + sb.WriteString(firstConjunction) + + for i, item := range params { + switch i { + case len(params) - 1: + sb.WriteString(lastConjunction) + sb.WriteString(item) + sb.WriteRune(' ') + default: + sb.WriteString(item) + sb.WriteString(", ") + } + + } + sb.WriteString("are set") + return active, sb.String() +} diff --git a/integrations/terraform/provider/credentials_test.go b/integrations/terraform/provider/credentials_test.go new file mode 100644 index 0000000000000..528a878e348ff --- /dev/null +++ b/integrations/terraform/provider/credentials_test.go @@ -0,0 +1,118 @@ +/* +Copyright 2024 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provider + +import ( + "context" + "testing" + + "github.com/gravitational/trace" + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/api/client" +) + +func TestActiveSources(t *testing.T) { + ctx := context.Background() + + activeSource1 := fakeActiveCredentialsSource{"active1"} + activeSource2 := fakeActiveCredentialsSource{"active2"} + inactiveSource1 := fakeInactiveCredentialsSource{"inactive1"} + inactiveSource2 := fakeInactiveCredentialsSource{"inactive2"} + + tests := []struct { + name string + sources CredentialSources + expectedSources CredentialSources + wantErr bool + }{ + { + name: "no source", + sources: CredentialSources{}, + expectedSources: nil, + wantErr: true, + }, + { + name: "no active source", + sources: CredentialSources{ + inactiveSource1, + inactiveSource2, + }, + expectedSources: nil, + wantErr: true, + }, + { + name: "single active source", + sources: CredentialSources{ + activeSource1, + }, + expectedSources: CredentialSources{activeSource1}, + wantErr: false, + }, + { + name: "multiple active and inactive sources", + sources: CredentialSources{ + inactiveSource1, + activeSource1, + inactiveSource2, + activeSource2, + }, + expectedSources: CredentialSources{activeSource1, activeSource2}, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, diags := tt.sources.ActiveSources(ctx, providerData{}) + require.Equal(t, tt.wantErr, diags.HasError()) + require.Equal(t, tt.expectedSources, result) + }) + } +} + +type fakeActiveCredentialsSource struct { + name string +} + +func (f fakeActiveCredentialsSource) Name() string { + return f.name +} + +func (f fakeActiveCredentialsSource) IsActive(data providerData) (bool, string) { + return true, "" +} + +func (f fakeActiveCredentialsSource) Credentials(ctx context.Context, data providerData) (client.Credentials, error) { + return nil, trace.NotImplemented("not implemented") +} + +type fakeInactiveCredentialsSource struct { + name string +} + +func (f fakeInactiveCredentialsSource) Name() string { + return f.name +} + +func (f fakeInactiveCredentialsSource) IsActive(data providerData) (bool, string) { + return false, "" +} + +func (f fakeInactiveCredentialsSource) Credentials(ctx context.Context, data providerData) (client.Credentials, error) { + return nil, trace.NotImplemented("not implemented") +} diff --git a/integrations/terraform/provider/provider.go b/integrations/terraform/provider/provider.go index df2d1eeead643..a3e54686f5e93 100644 --- a/integrations/terraform/provider/provider.go +++ b/integrations/terraform/provider/provider.go @@ -18,13 +18,9 @@ package provider import ( "context" - "crypto/tls" - "crypto/x509" - "encoding/base64" "fmt" "net" "os" - "path/filepath" "strconv" "strings" "time" @@ -47,6 +43,51 @@ const ( minServerVersion = "15.0.0-0" ) +const ( + // attributeTerraformAddress is the attribute configuring the Teleport address the Terraform provider connects to. + attributeTerraformAddress = "addr" + // attributeTerraformCertificates is the attribute configuring the path the Terraform provider loads its + // client certificates from. This only works for direct auth joining. + attributeTerraformCertificates = "cert_path" + // attributeTerraformCertificatesBase64 is the attribute configuring the client certificates used by the + // Terraform provider. This only works for direct auth joining. + attributeTerraformCertificatesBase64 = "cert_base64" + // attributeTerraformKey is the attribute configuring the path the Terraform provider loads its + // client key from. This only works for direct auth joining. + attributeTerraformKey = "key_path" + // attributeTerraformKeyBase64 is the attribute configuring the client key used by the + // Terraform provider. This only works for direct auth joining. + attributeTerraformKeyBase64 = "key_base64" + // attributeTerraformRootCertificates is the attribute configuring the path the Terraform provider loads its + // trusted CA certificates from. This only works for direct auth joining. + attributeTerraformRootCertificates = "root_ca_path" + // attributeTerraformRootCertificatesBase64 is the attribute configuring the CA certificates trusted by the + // Terraform provider. This only works for direct auth joining. + attributeTerraformRootCertificatesBase64 = "root_ca_base64" + // attributeTerraformProfileName is the attribute containing name of the profile used by the Terraform provider. + attributeTerraformProfileName = "profile_name" + // attributeTerraformProfilePath is the attribute containing the profile directory used by the Terraform provider. + attributeTerraformProfilePath = "profile_dir" + // attributeTerraformIdentityFilePath is the attribute containing the path to the identity file used by the provider. + attributeTerraformIdentityFilePath = "identity_file_path" + // attributeTerraformIdentityFile is the attribute containing the identity file used by the Terraform provider. + attributeTerraformIdentityFile = "identity_file" + // attributeTerraformIdentityFileBase64 is the attribute containing the base64-encoded identity file used by the Terraform provider. + attributeTerraformIdentityFileBase64 = "identity_file_base64" + // attributeTerraformRetryBaseDuration is the attribute configuring the base duration between two Terraform provider retries. + attributeTerraformRetryBaseDuration = "retry_base_duration" + // attributeTerraformRetryCapDuration is the attribute configuring the maximum duration between two Terraform provider retries. + attributeTerraformRetryCapDuration = "retry_cap_duration" + // attributeTerraformRetryMaxTries is the attribute configuring the maximum number of Terraform provider retries. + attributeTerraformRetryMaxTries = "retry_max_tries" + // attributeTerraformDialTimeoutDuration is the attribute configuring the Terraform provider dial timeout. + attributeTerraformDialTimeoutDuration = "dial_timeout_duration" + // attributeTerraformJoinMethod is the attribute configuring the Terraform provider native MachineID join method. + attributeTerraformJoinMethod = "join_method" + // attributeTerraformJoinToken is the attribute configuring the Terraform provider native MachineID join token. + attributeTerraformJoinToken = "join_token" +) + type RetryConfig struct { Base time.Duration Cap time.Duration @@ -58,6 +99,7 @@ type Provider struct { configured bool Client *client.Client RetryConfig RetryConfig + cancel context.CancelFunc } // providerData provider schema struct @@ -94,6 +136,10 @@ type providerData struct { RetryMaxTries types.String `tfsdk:"retry_max_tries"` // DialTimeout sets timeout when trying to connect to the server. DialTimeoutDuration types.String `tfsdk:"dial_timeout_duration"` + // JoinMethod is the MachineID join method. + JoinMethod types.String `tfsdk:"join_method"` + // JoinMethod is the MachineID join token. + JoinToken types.String `tfsdk:"join_token"` } // New returns an empty provider struct @@ -105,92 +151,104 @@ func New() tfsdk.Provider { func (p *Provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { return tfsdk.Schema{ Attributes: map[string]tfsdk.Attribute{ - "addr": { + attributeTerraformAddress: { Type: types.StringType, Optional: true, - Description: "host:port where Teleport Auth Service is running. This can also be set with the environment variable `TF_TELEPORT_ADDR`.", + Description: fmt.Sprintf("host:port of the Teleport address. This can be the Teleport Proxy Service address (port 443 or 4080) or the Teleport Auth Service address (port 3025). This can also be set with the environment variable `%s`.", constants.EnvVarTerraformAddress), }, - "cert_path": { + attributeTerraformCertificates: { Type: types.StringType, Optional: true, - Description: "Path to Teleport auth certificate file. This can also be set with the environment variable `TF_TELEPORT_CERT`.", + Description: fmt.Sprintf("Path to Teleport auth certificate file. This can also be set with the environment variable `%s`.", constants.EnvVarTerraformCertificates), }, - "cert_base64": { + attributeTerraformCertificatesBase64: { Type: types.StringType, Optional: true, - Description: "Base64 encoded TLS auth certificate. This can also be set with the environment variable `TF_TELEPORT_CERT_BASE64`.", + Description: fmt.Sprintf("Base64 encoded TLS auth certificate. This can also be set with the environment variable `%s`.", constants.EnvVarTerraformCertificatesBase64), }, - "key_path": { + attributeTerraformKey: { Type: types.StringType, Optional: true, - Description: "Path to Teleport auth key file. This can also be set with the environment variable `TF_TELEPORT_KEY`.", + Description: fmt.Sprintf("Path to Teleport auth key file. This can also be set with the environment variable `%s`.", constants.EnvVarTerraformKey), }, - "key_base64": { + attributeTerraformKeyBase64: { Type: types.StringType, Sensitive: true, Optional: true, - Description: "Base64 encoded TLS auth key. This can also be set with the environment variable `TF_TELEPORT_KEY_BASE64`.", + Description: fmt.Sprintf("Base64 encoded TLS auth key. This can also be set with the environment variable `%s`.", constants.EnvVarTerraformKeyBase64), }, - "root_ca_path": { + attributeTerraformRootCertificates: { Type: types.StringType, Optional: true, - Description: "Path to Teleport Root CA. This can also be set with the environment variable `TF_TELEPORT_ROOT_CA`.", + Description: fmt.Sprintf("Path to Teleport Root CA. This can also be set with the environment variable `%s`.", constants.EnvVarTerraformRootCertificates), }, - "root_ca_base64": { + attributeTerraformRootCertificatesBase64: { Type: types.StringType, Optional: true, - Description: "Base64 encoded Root CA. This can also be set with the environment variable `TF_TELEPORT_CA_BASE64`.", + Description: fmt.Sprintf("Base64 encoded Root CA. This can also be set with the environment variable `%s`.", constants.EnvVarTerraformRootCertificatesBase64), }, - "profile_name": { + attributeTerraformProfileName: { Type: types.StringType, Optional: true, - Description: "Teleport profile name. This can also be set with the environment variable `TF_TELEPORT_PROFILE_NAME`.", + Description: fmt.Sprintf("Teleport profile name. This can also be set with the environment variable `%s`.", constants.EnvVarTerraformProfileName), }, - "profile_dir": { + attributeTerraformProfilePath: { Type: types.StringType, Optional: true, - Description: "Teleport profile path. This can also be set with the environment variable `TF_TELEPORT_PROFILE_PATH`.", + Description: fmt.Sprintf("Teleport profile path. This can also be set with the environment variable `%s`.", constants.EnvVarTerraformProfilePath), }, - "identity_file_path": { + attributeTerraformIdentityFilePath: { Type: types.StringType, Optional: true, - Description: "Teleport identity file path. This can also be set with the environment variable `TF_TELEPORT_IDENTITY_FILE_PATH`.", + Description: fmt.Sprintf("Teleport identity file path. This can also be set with the environment variable `%s`.", constants.EnvVarTerraformIdentityFilePath), }, - "identity_file": { + attributeTerraformIdentityFile: { Type: types.StringType, Sensitive: true, Optional: true, - Description: "Teleport identity file content. This can also be set with the environment variable `TF_TELEPORT_IDENTITY_FILE`.", + Description: fmt.Sprintf("Teleport identity file content. This can also be set with the environment variable `%s`.", constants.EnvVarTerraformIdentityFile), }, - "identity_file_base64": { + attributeTerraformIdentityFileBase64: { Type: types.StringType, Sensitive: true, Optional: true, - Description: "Teleport identity file content base64 encoded. This can also be set with the environment variable `TF_TELEPORT_IDENTITY_FILE_BASE64`.", + Description: fmt.Sprintf("Teleport identity file content base64 encoded. This can also be set with the environment variable `%s`.", constants.EnvVarTerraformIdentityFileBase64), + }, + attributeTerraformRetryBaseDuration: { + Type: types.StringType, + Sensitive: false, + Optional: true, + Description: fmt.Sprintf("Retry algorithm when the API returns 'not found': base duration between retries (https://pkg.go.dev/time#ParseDuration). This can also be set with the environment variable `%s`.", constants.EnvVarTerraformRetryBaseDuration), }, - "retry_base_duration": { + attributeTerraformRetryCapDuration: { Type: types.StringType, Sensitive: false, Optional: true, - Description: "Retry algorithm when the API returns 'not found': base duration between retries (https://pkg.go.dev/time#ParseDuration). This can also be set with the environment variable `TF_TELEPORT_RETRY_BASE_DURATION`.", + Description: fmt.Sprintf("Retry algorithm when the API returns 'not found': max duration between retries (https://pkg.go.dev/time#ParseDuration). This can also be set with the environment variable `%s`.", constants.EnvVarTerraformRetryCapDuration), }, - "retry_cap_duration": { + attributeTerraformRetryMaxTries: { Type: types.StringType, Sensitive: false, Optional: true, - Description: "Retry algorithm when the API returns 'not found': max duration between retries (https://pkg.go.dev/time#ParseDuration). This can also be set with the environment variable `TF_TELEPORT_RETRY_CAP_DURATION`.", + Description: fmt.Sprintf("Retry algorithm when the API returns 'not found': max tries. This can also be set with the environment variable `%s`.", constants.EnvVarTerraformRetryMaxTries), }, - "retry_max_tries": { + attributeTerraformDialTimeoutDuration: { Type: types.StringType, Sensitive: false, Optional: true, - Description: "Retry algorithm when the API returns 'not found': max tries. This can also be set with the environment variable `TF_TELEPORT_RETRY_MAX_TRIES`.", + Description: fmt.Sprintf("DialTimeout sets timeout when trying to connect to the server. This can also be set with the environment variable `%s`.", constants.EnvVarTerraformDialTimeoutDuration), }, - "dial_timeout_duration": { + attributeTerraformJoinMethod: { Type: types.StringType, Sensitive: false, Optional: true, - Description: "DialTimeout sets timeout when trying to connect to the server. This can also be set with the environment variable `TF_TELEPORT_DIAL_TIMEOUT_DURATION`.", + Description: fmt.Sprintf("Enables the native Terraform MachineID support. When set, Terraform uses MachineID to securely join the Teleport cluster and obtain credentials. See [the join method reference](./join-methods.mdx) for possible values, you must use [a delegated join method](./join-methods.mdx#secret-vs-delegated). This can also be set with the environment variable `%s`.", constants.EnvVarTerraformJoinMethod), + }, + attributeTerraformJoinToken: { + Type: types.StringType, + Sensitive: false, + Optional: true, + Description: fmt.Sprintf("Name of the token used for the native MachineID joining. This value is not sensitive for [delegated join methods](./join-methods.mdx#secret-vs-delegated). This can also be set with the environment variable `%s`.", constants.EnvVarTerraformJoinToken), }, }, }, nil @@ -210,10 +268,15 @@ func (p *Provider) IsConfigured(diags diag.Diagnostics) bool { // Configure configures the Teleport client func (p *Provider) Configure(ctx context.Context, req tfsdk.ConfigureProviderRequest, resp *tfsdk.ConfigureProviderResponse) { - var creds []client.Credentials - p.configureLog() + // We wrap the provider's context into a cancellable one. + // This allows us to cancel the context and properly close the client and any background task potentially running + // (e.g. MachineID bot renewing creds). This is required during the tests as the provider is run multiple times. + // You can cancel the context by calling Provider.Close() + ctx, cancel := context.WithCancel(ctx) + p.cancel = cancel + var config providerData diags := req.Config.Get(ctx, &config) resp.Diagnostics.Append(diags...) @@ -221,22 +284,11 @@ func (p *Provider) Configure(ctx context.Context, req tfsdk.ConfigureProviderReq return } - addr := p.stringFromConfigOrEnv(config.Addr, constants.EnvVarTerraformAddress, "") - certPath := p.stringFromConfigOrEnv(config.CertPath, constants.EnvVarTerraformCertificates, "") - certBase64 := p.stringFromConfigOrEnv(config.CertBase64, constants.EnvVarTerraformCertificatesBase64, "") - keyPath := p.stringFromConfigOrEnv(config.KeyPath, constants.EnvVarTerraformKey, "") - keyBase64 := p.stringFromConfigOrEnv(config.KeyBase64, constants.EnvVarTerraformKeyBase64, "") - caPath := p.stringFromConfigOrEnv(config.RootCaPath, constants.EnvVarTerraformRootCertificates, "") - caBase64 := p.stringFromConfigOrEnv(config.RootCaBase64, constants.EnvVarTerraformRootCertificatesBase64, "") - profileName := p.stringFromConfigOrEnv(config.ProfileName, constants.EnvVarTerraformProfileName, "") - profileDir := p.stringFromConfigOrEnv(config.ProfileDir, constants.EnvVarTerraformProfilePath, "") - identityFilePath := p.stringFromConfigOrEnv(config.IdentityFilePath, constants.EnvVarTerraformIdentityFilePath, "") - identityFile := p.stringFromConfigOrEnv(config.IdentityFile, constants.EnvVarTerraformIdentityFile, "") - identityFileBase64 := p.stringFromConfigOrEnv(config.IdentityFileBase64, constants.EnvVarTerraformIdentityFileBase64, "") - retryBaseDurationStr := p.stringFromConfigOrEnv(config.RetryBaseDuration, constants.EnvVarTerraformRetryBaseDuration, "1s") - retryCapDurationStr := p.stringFromConfigOrEnv(config.RetryCapDuration, constants.EnvVarTerraformRetryCapDuration, "5s") - maxTriesStr := p.stringFromConfigOrEnv(config.RetryMaxTries, constants.EnvVarTerraformRetryMaxTries, "10") - dialTimeoutDurationStr := p.stringFromConfigOrEnv(config.DialTimeoutDuration, constants.EnvVarTerraformDialTimeoutDuration, "30s") + addr := stringFromConfigOrEnv(config.Addr, constants.EnvVarTerraformAddress, "") + retryBaseDurationStr := stringFromConfigOrEnv(config.RetryBaseDuration, constants.EnvVarTerraformRetryBaseDuration, "1s") + retryCapDurationStr := stringFromConfigOrEnv(config.RetryCapDuration, constants.EnvVarTerraformRetryCapDuration, "5s") + maxTriesStr := stringFromConfigOrEnv(config.RetryMaxTries, constants.EnvVarTerraformRetryMaxTries, "10") + dialTimeoutDurationStr := stringFromConfigOrEnv(config.DialTimeoutDuration, constants.EnvVarTerraformDialTimeoutDuration, "30s") if !p.validateAddr(addr, resp) { return @@ -244,83 +296,26 @@ func (p *Provider) Configure(ctx context.Context, req tfsdk.ConfigureProviderReq log.WithFields(log.Fields{"addr": addr}).Debug("Using Teleport address") - if certPath != "" && keyPath != "" { - l := log.WithField("cert_path", certPath).WithField("key_path", keyPath).WithField("root_ca_path", caPath) - l.Debug("Using auth with certificate, private key and (optionally) CA read from files") - - cred, ok := p.getCredentialsFromKeyPair(certPath, keyPath, caPath, resp) - if !ok { - return - } - creds = append(creds, cred) - } - - if certBase64 != "" && keyBase64 != "" { - log.Debug("Using auth with certificate, private key and (optionally) CA read from base64 encoded vars") - cred, ok := p.getCredentialsFromBase64(certBase64, keyBase64, caBase64, resp) - if !ok { - return - } - creds = append(creds, cred) - } - - if identityFilePath != "" { - log.WithField("identity_file_path", identityFilePath).Debug("Using auth with identity file") - - if !p.fileExists(identityFilePath) { - resp.Diagnostics.AddError( - "Identity file not found", - fmt.Sprintf( - "File %v not found! Use `tctl auth sign --user=example@example.com --format=file --out=%v` to generate identity file", - identityFilePath, - identityFilePath, - ), - ) - return - } - - creds = append(creds, client.LoadIdentityFile(identityFilePath)) - } - - if identityFile != "" { - log.Debug("Using auth from identity file provided with environment variable TF_TELEPORT_IDENTITY_FILE") - creds = append(creds, client.LoadIdentityFileFromString(identityFile)) - } - - if identityFileBase64 != "" { - log.Debug("Using auth from base64 encoded identity file provided with environment variable TF_TELEPORT_IDENTITY_FILE_BASE64") - decoded, err := base64.StdEncoding.DecodeString(identityFileBase64) - if err != nil { - resp.Diagnostics.AddError( - "Failed to decode Identity file using base 64", - fmt.Sprintf("Error when trying to decode: %v", err), - ) - return - } - - creds = append(creds, client.LoadIdentityFileFromString(string(decoded))) - } - - if profileDir != "" || len(creds) == 0 { - log.WithFields(log.Fields{ - "dir": profileDir, - "name": profileName, - }).Debug("Using profile as the default auth method") - creds = append(creds, client.LoadProfile(profileDir, profileName)) - } - dialTimeoutDuration, err := time.ParseDuration(dialTimeoutDurationStr) if err != nil { resp.Diagnostics.AddError( "Failed to parse Dial Timeout Duration Cap Duration", - fmt.Sprintf("Please check if dial_timeout_duration (or TF_TELEPORT_DIAL_TIMEOUT_DURATION) is set correctly. Error: %s", err), + fmt.Sprintf( + "Please check if %s (or %s) is set correctly. Error: %s", + attributeTerraformDialTimeoutDuration, constants.EnvVarTerraformDialTimeoutDuration, err, + ), ) return } - client, err := client.New(ctx, client.Config{ + activeSources, diags := supportedCredentialSources.ActiveSources(ctx, config) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + clientConfig := client.Config{ Addrs: []string{addr}, - Credentials: creds, DialTimeout: dialTimeoutDuration, DialOpts: []grpc.DialOption{ grpc.WithReturnConnectionError(), @@ -328,15 +323,15 @@ func (p *Provider) Configure(ctx context.Context, req tfsdk.ConfigureProviderReq grpc.WaitForReady(true), ), }, - }) + } - if err != nil { - log.WithError(err).Debug("Error connecting to Teleport!") - resp.Diagnostics.AddError("Error connecting to Teleport!", err.Error()) + clt, diags := activeSources.BuildClient(ctx, clientConfig, config) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { return } - if !p.checkTeleportVersion(ctx, client, resp) { + if !p.checkTeleportVersion(ctx, clt, resp) { return } @@ -344,7 +339,10 @@ func (p *Provider) Configure(ctx context.Context, req tfsdk.ConfigureProviderReq if err != nil { resp.Diagnostics.AddError( "Failed to parse Retry Base Duration", - fmt.Sprintf("Please check if retry_cap_duration (or TF_TELEPORT_RETRY_BASE_DURATION) is set correctly. Error: %s", err), + fmt.Sprintf( + "Please check if %s (or %s) is set correctly. Error: %s", + attributeTerraformRetryBaseDuration, constants.EnvVarTerraformRetryBaseDuration, err, + ), ) return } @@ -353,7 +351,10 @@ func (p *Provider) Configure(ctx context.Context, req tfsdk.ConfigureProviderReq if err != nil { resp.Diagnostics.AddError( "Failed to parse Retry Cap Duration", - fmt.Sprintf("Please check if retry_cap_duration (or TF_TELEPORT_RETRY_CAP_DURATION) is set correctly. Error: %s", err), + fmt.Sprintf( + "Please check if %s (or %s) is set correctly. Error: %s", + attributeTerraformRetryCapDuration, constants.EnvVarTerraformRetryCapDuration, err, + ), ) return } @@ -362,7 +363,10 @@ func (p *Provider) Configure(ctx context.Context, req tfsdk.ConfigureProviderReq if err != nil { resp.Diagnostics.AddError( "Failed to parse Retry Max Tries", - fmt.Sprintf("Please check if retry_max_tries (or TF_TELEPORT_RETRY_MAX_TRIES) is set correctly. Error: %s", err), + fmt.Sprintf( + "Please check if %s (or %s) is set correctly. Error: %s", + attributeTerraformRetryMaxTries, constants.EnvVarTerraformRetryMaxTries, err, + ), ) return } @@ -372,7 +376,7 @@ func (p *Provider) Configure(ctx context.Context, req tfsdk.ConfigureProviderReq Cap: retryCapDuration, MaxTries: int(maxTries), } - p.Client = client + p.Client = clt p.configured = true } @@ -402,7 +406,7 @@ func (p *Provider) checkTeleportVersion(ctx context.Context, client *client.Clie } // stringFromConfigOrEnv returns value from config or from env var if config value is empty, default otherwise -func (p *Provider) stringFromConfigOrEnv(value types.String, env string, def string) string { +func stringFromConfigOrEnv(value types.String, env string, def string) string { if value.Unknown || value.Null { value := os.Getenv(env) if value != "" { @@ -424,109 +428,25 @@ func (p *Provider) validateAddr(addr string, resp *tfsdk.ConfigureProviderRespon if addr == "" { resp.Diagnostics.AddError( "Teleport address is empty", - "Please, specify either TF_TELEPORT_ADDR or addr in provider configuration", + fmt.Sprintf("Please, specify either %s in provider configuration, or the %s environment variable", + attributeTerraformAddress, constants.EnvVarTerraformAddress), ) return false } _, _, err := net.SplitHostPort(addr) if err != nil { - log.WithField("addr", addr).WithError(err).Debug("Teleport addr format error!") + log.WithField("addr", addr).WithError(err).Debug("Teleport address format error!") resp.Diagnostics.AddError( - "Invalid Teleport addr format", - "Teleport addr must be specified as host:port", + "Invalid Teleport address format", + fmt.Sprintf("Teleport address must be specified as host:port. Got %q", addr), ) return false } return true } -// getCredentialsFromBase64 returns client.Credentials built from base64 encoded keys -func (p *Provider) getCredentialsFromBase64(certBase64, keyBase64, caBase64 string, resp *tfsdk.ConfigureProviderResponse) (client.Credentials, bool) { - cert, err := base64.StdEncoding.DecodeString(certBase64) - if err != nil { - resp.Diagnostics.AddError( - "Failed to base64 decode cert", - fmt.Sprintf("Please check if cert_base64 (or TF_TELEPORT_CERT_BASE64) is set correctly. Error: %s", err), - ) - return nil, false - } - key, err := base64.StdEncoding.DecodeString(keyBase64) - if err != nil { - resp.Diagnostics.AddError( - "Failed to base64 decode key", - fmt.Sprintf("Please check if key_base64 (or TF_TELEPORT_KEY_BASE64) is set correctly. Error: %s", err), - ) - return nil, false - } - rootCa, err := base64.StdEncoding.DecodeString(caBase64) - if err != nil { - resp.Diagnostics.AddError( - "Failed to base64 decode root ca", - fmt.Sprintf("Please check if root_ca_base64 (or TF_TELEPORT_CA_BASE64) is set correctly. Error: %s", err), - ) - return nil, false - } - tlsConfig, err := createTLSConfig(cert, key, rootCa) - if err != nil { - resp.Diagnostics.AddError( - "Failed to create TLS config", - fmt.Sprintf("Error: %s", err), - ) - return nil, false - } - return client.LoadTLS(tlsConfig), true -} - -// getCredentialsFromKeyPair returns client.Credentials built from path to key files -func (p *Provider) getCredentialsFromKeyPair(certPath string, keyPath string, caPath string, resp *tfsdk.ConfigureProviderResponse) (client.Credentials, bool) { - if !p.fileExists(certPath) { - resp.Diagnostics.AddError( - "Certificate file not found", - fmt.Sprintf("File %v not found! Use 'tctl auth sign --user=example@example.com --format=tls --out=%v' to generate keys", - certPath, - filepath.Dir(certPath), - ), - ) - return nil, false - } - - if !p.fileExists(keyPath) { - resp.Diagnostics.AddError( - "Private key file not found", - fmt.Sprintf("File %v not found! Use 'tctl auth sign --user=example@example.com --format=tls --out=%v' to generate keys", - keyPath, - filepath.Dir(keyPath), - ), - ) - return nil, false - } - - if !p.fileExists(caPath) { - resp.Diagnostics.AddError( - "Root CA certificate file not found", - fmt.Sprintf("File %v not found! Use 'tctl auth sign --user=example@example.com --format=tls --out=%v' to generate keys", - caPath, - filepath.Dir(caPath), - ), - ) - return nil, false - } - - return client.LoadKeyPair(certPath, keyPath, caPath), true -} - -// fileExists returns true if file exists -func (p *Provider) fileExists(path string) bool { - _, err := os.Stat(path) - if os.IsNotExist(err) { - return false - } - if err != nil { - return false - } - return true -} +// TODO(hugoShaka): fix logging in a future release by converting to tflog. // configureLog configures logging func (p *Provider) configureLog() { @@ -547,21 +467,6 @@ func (p *Provider) configureLog() { } } -// createTLSConfig returns tls.Config build from keys -func createTLSConfig(cert, key, rootCa []byte) (*tls.Config, error) { - keyPair, err := tls.X509KeyPair(cert, key) - if err != nil { - return nil, err - } - caCertPool := x509.NewCertPool() - caCertPool.AppendCertsFromPEM(rootCa) - - return &tls.Config{ - Certificates: []tls.Certificate{keyPair}, - RootCAs: caCertPool, - }, nil -} - // GetResources returns the map of provider resources func (p *Provider) GetResources(_ context.Context) (map[string]tfsdk.ResourceType, diag.Diagnostics) { return map[string]tfsdk.ResourceType{ @@ -609,3 +514,16 @@ func (p *Provider) GetDataSources(_ context.Context) (map[string]tfsdk.DataSourc "teleport_access_list": dataSourceTeleportAccessListType{}, }, nil } + +// Close closes the provider's client and cancels its context. +// This is needed in the tests to avoid accumulating clients and running out of file descriptors. +func (p *Provider) Close() error { + var err error + if p.Client != nil { + err = p.Client.Close() + } + if p.cancel != nil { + p.cancel() + } + return err +} diff --git a/integrations/terraform/templates/data-sources-index.mdx.tmpl b/integrations/terraform/templates/data-sources-index.mdx.tmpl index 8bc7418774b81..c4c7b90af7525 100644 --- a/integrations/terraform/templates/data-sources-index.mdx.tmpl +++ b/integrations/terraform/templates/data-sources-index.mdx.tmpl @@ -3,6 +3,9 @@ title: "Terraform data-sources index" description: "Index of all the data-sources supported by the Teleport Terraform Provider" --- +{{ "{/*Auto-generated file. Do not edit.*/}" }} +{{ "{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/}" }} + {/* This file will be renamed data-sources.mdx during build time. The template name is reserved by tfplugindocs so we suffix with -index. diff --git a/integrations/terraform/templates/data-sources.md.tmpl b/integrations/terraform/templates/data-sources.md.tmpl index e7484ce3fefd4..8b479dbd8cd96 100644 --- a/integrations/terraform/templates/data-sources.md.tmpl +++ b/integrations/terraform/templates/data-sources.md.tmpl @@ -3,6 +3,9 @@ title: Reference for the {{.Name}} Terraform data-source description: This page describes the supported values of the {{.Name}} data-source of the Teleport Terraform provider. --- +{{ "{/*Auto-generated file. Do not edit.*/}" }} +{{ "{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/}" }} + {{ .Description | trimspace }} {{ if .HasExample -}} diff --git a/integrations/terraform/templates/index.md.tmpl b/integrations/terraform/templates/index.md.tmpl index 8cb87c15b85da..924402d1a0147 100644 --- a/integrations/terraform/templates/index.md.tmpl +++ b/integrations/terraform/templates/index.md.tmpl @@ -3,6 +3,9 @@ title: "Teleport Terraform Provider" description: Reference documentation of the Teleport Terraform provider. --- +{{ "{/*Auto-generated file. Do not edit.*/}" }} +{{ "{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/}" }} + The Teleport Terraform provider allows Terraform users to configure Teleport from Terraform. diff --git a/integrations/terraform/templates/resources-index.mdx.tmpl b/integrations/terraform/templates/resources-index.mdx.tmpl index b48205cbf37fb..42f5821dfbca9 100644 --- a/integrations/terraform/templates/resources-index.mdx.tmpl +++ b/integrations/terraform/templates/resources-index.mdx.tmpl @@ -3,6 +3,9 @@ title: "Terraform resources index" description: "Index of all the datasources supported by the Teleport Terraform Provider" --- +{{ "{/*Auto-generated file. Do not edit.*/}" }} +{{ "{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/}" }} + {/* This file will be renamed data-sources.mdx during build time. The template name is reserved by tfplugindocs so we suffix with -index. diff --git a/integrations/terraform/templates/resources.md.tmpl b/integrations/terraform/templates/resources.md.tmpl index 70ae5a563368a..d80255a7e4534 100644 --- a/integrations/terraform/templates/resources.md.tmpl +++ b/integrations/terraform/templates/resources.md.tmpl @@ -3,6 +3,9 @@ title: Reference for the {{.Name}} Terraform resource description: This page describes the supported values of the {{.Name}} resource of the Teleport Terraform provider. --- +{{ "{/*Auto-generated file. Do not edit.*/}" }} +{{ "{/*To regenerate, navigate to integrations/terraform and run `make docs`.*/}" }} + {{ .Description | trimspace }} {{ if .HasExample -}} diff --git a/integrations/terraform/testlib/machineid_join_test.go b/integrations/terraform/testlib/machineid_join_test.go new file mode 100644 index 0000000000000..52299c3cf457e --- /dev/null +++ b/integrations/terraform/testlib/machineid_join_test.go @@ -0,0 +1,164 @@ +/* +Copyright 2024 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package testlib + +import ( + "context" + "fmt" + "os" + "path/filepath" + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-framework/providerserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/jonboulle/clockwork" + "github.com/stretchr/testify/require" + + headerv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + machineidv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/machineid/v1" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/integrations/lib/testing/fakejoin" + "github.com/gravitational/teleport/integrations/lib/testing/integration" + "github.com/gravitational/teleport/lib/kubernetestoken" + "github.com/gravitational/teleport/lib/services" + + "github.com/gravitational/teleport/integrations/terraform/provider" +) + +func TestTerraformJoin(t *testing.T) { + require.NoError(t, os.Setenv("TF_ACC", "true")) + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + // Test setup: start a Telpeort auth server + authHelper := &integration.MinimalAuthHelper{} + clt := authHelper.StartServer(t) + + var err error + // Test setup: create the terraform role + tfRole := services.NewPresetTerraformProviderRole() + tfRole, err = clt.CreateRole(ctx, tfRole) + require.NoError(t, err) + + // Test setup: create a fake Kubernetes signer that will allow us to use the kubernetes/jwks join method + clock := clockwork.NewRealClock() + signer, err := fakejoin.NewKubernetesSigner(clock) + require.NoError(t, err) + + jwks, err := signer.GetMarshaledJWKS() + require.NoError(t, err) + + // Test setup: create a token and a bot that can join the cluster with JWT signed by our fake Kubernetes signer + testBotName := "testBot" + testTokenName := "testToken" + fakeNamespace := "test-namespace" + fakeServiceAccount := "test-service-account" + token, err := types.NewProvisionTokenFromSpec( + testTokenName, + clock.Now().Add(time.Hour), + types.ProvisionTokenSpecV2{ + Roles: types.SystemRoles{types.RoleBot}, + JoinMethod: types.JoinMethodKubernetes, + BotName: testBotName, + Kubernetes: &types.ProvisionTokenSpecV2Kubernetes{ + Allow: []*types.ProvisionTokenSpecV2Kubernetes_Rule{ + { + ServiceAccount: fmt.Sprintf("%s:%s", fakeNamespace, fakeServiceAccount), + }, + }, + Type: types.KubernetesJoinTypeStaticJWKS, + StaticJWKS: &types.ProvisionTokenSpecV2Kubernetes_StaticJWKSConfig{ + JWKS: jwks, + }, + }, + }) + require.NoError(t, err) + err = clt.CreateToken(ctx, token) + require.NoError(t, err) + + bot := &machineidv1.Bot{ + Metadata: &headerv1.Metadata{ + Name: testBotName, + }, + Spec: &machineidv1.BotSpec{ + Roles: []string{tfRole.GetName()}, + }, + } + _, err = clt.BotServiceClient().CreateBot(ctx, &machineidv1.CreateBotRequest{Bot: bot}) + require.NoError(t, err) + + // Test setup: sign a Kube JWT for our bot to join the cluster + // We sign the token, write it to a temporary file, and point the embedded tbot to it + // with an environment variable. + pong, err := clt.Ping(ctx) + require.NoError(t, err) + clusterName := pong.ClusterName + jwt, err := signer.SignServiceAccountJWT("pod-name-doesnt-matter", fakeNamespace, fakeServiceAccount, clusterName) + require.NoError(t, err) + + tempDir := t.TempDir() + jwtPath := filepath.Join(tempDir, "token") + require.NoError(t, os.WriteFile(jwtPath, []byte(jwt), 0600)) + require.NoError(t, os.Setenv(kubernetestoken.EnvVarCustomKubernetesTokenPath, jwtPath)) + + // Test setup: craft a Terraform provider configuration + terraformConfig := fmt.Sprintf(` + provider "teleport" { + addr = %q + join_token = %q + join_method = %q + retry_base_duration = "900ms" + retry_cap_duration = "4s" + retry_max_tries = "12" + } + `, authHelper.ServerAddr(), testTokenName, types.JoinMethodKubernetes) + + terraformProvider := provider.New() + terraformProviders := make(map[string]func() (tfprotov6.ProviderServer, error)) + terraformProviders["teleport"] = func() (tfprotov6.ProviderServer, error) { + // Terraform configures provider on every test step, but does not clean up previous one, which produces + // to "too many open files" at some point. + // + // With this statement we try to forcefully close previously opened client, which stays cached in + // the provider variable. + p, ok := terraformProvider.(*provider.Provider) + require.True(t, ok) + require.NoError(t, p.Close()) + return providerserver.NewProtocol6(terraformProvider)(), nil + } + + // Test execution: apply a TF resource with the provider joining via MachineID + dummyResource, err := fixtures.ReadFile(filepath.Join("fixtures", "app_0_create.tf")) + require.NoError(t, err) + testConfig := terraformConfig + "\n" + string(dummyResource) + name := "teleport_app.test" + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: terraformProviders, + Steps: []resource.TestStep{ + { + Config: testConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name, "kind", "app"), + resource.TestCheckResourceAttr(name, "spec.uri", "localhost:3000"), + ), + }, + }, + }) +} diff --git a/integrations/terraform/testlib/main_test.go b/integrations/terraform/testlib/main_test.go index c6434a8c20cf2..e9ca6b361ca07 100644 --- a/integrations/terraform/testlib/main_test.go +++ b/integrations/terraform/testlib/main_test.go @@ -213,9 +213,7 @@ func (s *TerraformBaseSuite) closeClient() { s.T().Helper() p, ok := s.terraformProvider.(*provider.Provider) require.True(s.T(), ok) - if p != nil && p.Client != nil { - require.NoError(s.T(), p.Client.Close()) - } + require.NoError(s.T(), p.Close()) } // getFixture loads fixture and returns it as string or if failed diff --git a/integrations/terraform/tfschema/token/types_terraform.go b/integrations/terraform/tfschema/token/types_terraform.go index 2309b9cc1a5c3..9dc6722211bc1 100644 --- a/integrations/terraform/tfschema/token/types_terraform.go +++ b/integrations/terraform/tfschema/token/types_terraform.go @@ -366,7 +366,7 @@ func GenSchemaProvisionTokenV2(ctx context.Context) (github_com_hashicorp_terraf Optional: true, }, "join_method": { - Description: "JoinMethod is the joining method required in order to use this token. Supported joining methods include \"token\", \"ec2\", and \"iam\".", + Description: "JoinMethod is the joining method required in order to use this token. Supported joining methods include: azure, circleci, ec2, gcp, github, gitlab, iam, kubernetes, spacelift, token, tpm", Optional: true, Type: github_com_hashicorp_terraform_plugin_framework_types.StringType, }, diff --git a/lib/auth/auth_with_roles.go b/lib/auth/auth_with_roles.go index 1f2b5d06c2cb5..83bcd6099d310 100644 --- a/lib/auth/auth_with_roles.go +++ b/lib/auth/auth_with_roles.go @@ -1483,6 +1483,13 @@ func (a *ServerWithRoles) ListUnifiedResources(ctx context.Context, req *proto.L continue } r.Logins = logins + } else if d := r.GetAppServer(); d != nil { + logins, err := checker.GetAllowedLoginsForResource(d.GetApp()) + if err != nil { + log.WithError(err).WithField("resource", d.GetApp().GetName()).Warn("Unable to determine logins for app") + continue + } + r.Logins = logins } } } diff --git a/lib/auth/auth_with_roles_test.go b/lib/auth/auth_with_roles_test.go index 9cde844c32762..12f01e5dcc77a 100644 --- a/lib/auth/auth_with_roles_test.go +++ b/lib/auth/auth_with_roles_test.go @@ -3764,6 +3764,21 @@ func TestListResources_WithLogins(t *testing.T) { require.NoError(t, err) require.NoError(t, srv.Auth().UpsertWindowsDesktop(ctx, desktop)) + + awsApp, err := types.NewAppServerV3(types.Metadata{Name: name}, types.AppServerSpecV3{ + HostID: "_", + Hostname: "_", + App: &types.AppV3{ + Metadata: types.Metadata{Name: fmt.Sprintf("name-%d", i)}, + Spec: types.AppSpecV3{ + URI: "https://console.aws.amazon.com/ec2/v2/home", + }, + }, + }) + require.NoError(t, err) + + _, err = srv.Auth().UpsertApplicationServer(ctx, awsApp) + require.NoError(t, err) } // create user and client @@ -3772,6 +3787,7 @@ func TestListResources_WithLogins(t *testing.T) { require.NoError(t, err) role.SetWindowsDesktopLabels(types.Allow, types.Labels{types.Wildcard: []string{types.Wildcard}}) role.SetWindowsLogins(types.Allow, logins) + role.SetAWSRoleARNs(types.Allow, logins) _, err = srv.Auth().UpdateRole(ctx, role) require.NoError(t, err) @@ -3797,10 +3813,10 @@ func TestListResources_WithLogins(t *testing.T) { start = resp.NextKey } - // Check that only server and desktop resources contain the expected logins + // Check that only server, desktop, and app server resources contain the expected logins for _, resource := range results { switch resource.ResourceWithLabels.(type) { - case types.Server, types.WindowsDesktop: + case types.Server, types.WindowsDesktop, types.AppServer: require.Empty(t, cmp.Diff(resource.Logins, logins, cmpopts.SortSlices(func(a, b string) bool { return strings.Compare(a, b) < 0 }))) @@ -3829,10 +3845,10 @@ func TestListResources_WithLogins(t *testing.T) { start = resp.NextKey } - // Check that only server and desktop resources contain the expected logins + // Check that only server, desktop, and app server resources contain the expected logins for _, resource := range results { switch resource.ResourceWithLabels.(type) { - case types.Server, types.WindowsDesktop: + case types.Server, types.WindowsDesktop, types.AppServer: require.Empty(t, cmp.Diff(resource.Logins, logins, cmpopts.SortSlices(func(a, b string) bool { return strings.Compare(a, b) < 0 }))) @@ -4753,6 +4769,21 @@ func TestListUnifiedResources_WithLogins(t *testing.T) { require.NoError(t, err) require.NoError(t, srv.Auth().UpsertWindowsDesktop(ctx, desktop)) + + awsApp, err := types.NewAppServerV3(types.Metadata{Name: name}, types.AppServerSpecV3{ + HostID: "_", + Hostname: "_", + App: &types.AppV3{ + Metadata: types.Metadata{Name: fmt.Sprintf("name-%d", i)}, + Spec: types.AppSpecV3{ + URI: "https://console.aws.amazon.com/ec2/v2/home", + }, + }, + }) + require.NoError(t, err) + + _, err = srv.Auth().UpsertApplicationServer(ctx, awsApp) + require.NoError(t, err) } // create user and client @@ -4761,6 +4792,7 @@ func TestListUnifiedResources_WithLogins(t *testing.T) { require.NoError(t, err) role.SetWindowsDesktopLabels(types.Allow, types.Labels{types.Wildcard: []string{types.Wildcard}}) role.SetWindowsLogins(types.Allow, logins) + role.SetAWSRoleARNs(types.Allow, logins) _, err = srv.Auth().UpdateRole(ctx, role) require.NoError(t, err) @@ -4782,9 +4814,9 @@ func TestListUnifiedResources_WithLogins(t *testing.T) { start = resp.NextKey } - // Check that only server and desktop resources contain the expected logins + // Check that only server, desktop, and app server resources contain the expected logins for _, resource := range results { - if resource.GetNode() != nil || resource.GetWindowsDesktop() != nil { + if resource.GetNode() != nil || resource.GetWindowsDesktop() != nil || resource.GetAppServer() != nil { require.Empty(t, cmp.Diff(resource.Logins, logins, cmpopts.SortSlices(func(a, b string) bool { return strings.Compare(a, b) < 0 }))) diff --git a/lib/auth/bot.go b/lib/auth/bot.go index e8fe3b7ca5bee..99c132f469267 100644 --- a/lib/auth/bot.go +++ b/lib/auth/bot.go @@ -342,13 +342,15 @@ func newBotInstance( // care if the current identity is Nop. This function does not validate the // current identity at all; the caller is expected to validate that the client // is allowed to issue the (possibly renewable) certificates. +// +// Returns a second argument of the bot instance ID for inclusion in audit logs. func (a *Server) generateInitialBotCerts( ctx context.Context, botName, username, loginIP string, sshPubKey, tlsPubKey []byte, expires time.Time, renewable bool, initialAuth *machineidv1pb.BotInstanceStatusAuthentication, existingInstanceID string, -) (*proto.Certs, error) { +) (*proto.Certs, string, error) { var err error // Extract the user and role set for whom the certificate will be generated. @@ -360,13 +362,13 @@ func (a *Server) generateInitialBotCerts( userState, err := a.GetUserOrLoginState(ctx, username) if err != nil { log.WithError(err).Debugf("Could not impersonate user %v. The user could not be fetched from local store.", username) - return nil, trace.AccessDenied("access denied") + return nil, "", trace.AccessDenied("access denied") } // Do not allow SSO users to be impersonated. if userState.GetUserType() == types.UserTypeSSO { log.Warningf("Tried to issue a renewable cert for externally managed user %v, this is not supported.", username) - return nil, trace.AccessDenied("access denied") + return nil, "", trace.AccessDenied("access denied") } // Cap the cert TTL to the MaxRenewableCertTTL. @@ -378,11 +380,11 @@ func (a *Server) generateInitialBotCerts( accessInfo := services.AccessInfoFromUserState(userState) clusterName, err := a.GetClusterName() if err != nil { - return nil, trace.Wrap(err) + return nil, "", trace.Wrap(err) } checker, err := services.NewAccessChecker(accessInfo, clusterName.GetClusterName(), a) if err != nil { - return nil, trace.Wrap(err) + return nil, "", trace.Wrap(err) } // renewable cert request must include a generation @@ -412,7 +414,7 @@ func (a *Server) generateInitialBotCerts( // If no existing instance ID is known, create a new one. uuid, err := uuid.NewRandom() if err != nil { - return nil, trace.Wrap(err) + return nil, "", trace.Wrap(err) } bi := newBotInstance(&machineidv1pb.BotInstanceSpec{ @@ -422,7 +424,7 @@ func (a *Server) generateInitialBotCerts( _, err = a.BotInstance.CreateBotInstance(ctx, bi) if err != nil { - return nil, trace.Wrap(err) + return nil, "", trace.Wrap(err) } certReq.botInstanceID = uuid.String() @@ -433,7 +435,7 @@ func (a *Server) generateInitialBotCerts( // Note: botName is derived from the provision token rather than any // value sent by the client, so we can trust it. if err := a.updateBotInstance(ctx, &certReq, botName, existingInstanceID, initialAuth); err != nil { - return nil, trace.Wrap(err) + return nil, "", trace.Wrap(err) } // Only set the bot instance ID if it's empty; `updateBotInstance()` @@ -445,13 +447,13 @@ func (a *Server) generateInitialBotCerts( } if err := a.validateGenerationLabel(ctx, userState.GetName(), &certReq, 0); err != nil { - return nil, trace.Wrap(err) + return nil, "", trace.Wrap(err) } certs, err := a.generateUserCert(ctx, certReq) if err != nil { - return nil, trace.Wrap(err) + return nil, "", trace.Wrap(err) } - return certs, nil + return certs, certReq.botInstanceID, nil } diff --git a/lib/auth/bot_test.go b/lib/auth/bot_test.go index 5d2c4e96fd05d..b10d7c9e00fd9 100644 --- a/lib/auth/bot_test.go +++ b/lib/auth/bot_test.go @@ -33,6 +33,8 @@ import ( "github.com/aws/aws-sdk-go/aws/credentials" "github.com/digitorus/pkcs7" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/google/uuid" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" @@ -48,6 +50,7 @@ import ( machineidv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/machineid/v1" "github.com/gravitational/teleport/api/metadata" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/auth/join" "github.com/gravitational/teleport/lib/auth/machineid/machineidv1" @@ -55,6 +58,8 @@ import ( "github.com/gravitational/teleport/lib/auth/state" "github.com/gravitational/teleport/lib/auth/testauthority" "github.com/gravitational/teleport/lib/cloud/azure" + libevents "github.com/gravitational/teleport/lib/events" + "github.com/gravitational/teleport/lib/events/eventstest" "github.com/gravitational/teleport/lib/fixtures" "github.com/gravitational/teleport/lib/kubernetestoken" "github.com/gravitational/teleport/lib/reversetunnelclient" @@ -221,6 +226,9 @@ func TestRegisterBotInstance(t *testing.T) { }) srv := newTestTLSServer(t) + // Inject mockEmitter to capture audit events + mockEmitter := &eventstest.MockRecorderEmitter{} + srv.Auth().SetEmitter(mockEmitter) ctx := context.Background() _, err := CreateRole(ctx, srv.Auth(), "example", types.RoleSpecV6{}) @@ -287,6 +295,76 @@ func TestRegisterBotInstance(t *testing.T) { // only that record.) require.Len(t, botInstance.GetStatus().LatestAuthentications, 1) require.EqualExportedValues(t, ia, botInstance.GetStatus().LatestAuthentications[0]) + + // Validate that expected audit events were emitted... + auditEvents := mockEmitter.Events() + var joinEvent *events.BotJoin + for _, event := range auditEvents { + evt, ok := event.(*events.BotJoin) + if ok { + joinEvent = evt + break + } + } + require.NotNil(t, joinEvent) + require.Empty(t, + cmp.Diff(joinEvent, &events.BotJoin{ + Metadata: events.Metadata{ + Type: libevents.BotJoinEvent, + Code: libevents.BotJoinCode, + }, + Status: events.Status{ + Success: true, + }, + UserName: "bot-test", + BotName: "test", + Method: string(types.JoinMethodToken), + TokenName: token.GetSafeName(), + ConnectionMetadata: events.ConnectionMetadata{ + RemoteAddr: "127.0.0.1", + }, + BotInstanceID: ident.BotInstanceID, + }, + // There appears to be a bug with cmp.Diff and nil event.Struct that + // causes a panic so let's just ignore it. + cmpopts.IgnoreFields(events.BotJoin{}, "Attributes"), + cmpopts.IgnoreFields(events.Metadata{}, "Time"), + cmpopts.EquateEmpty(), + ), + ) + + var certIssueEvent *events.CertificateCreate + for _, event := range auditEvents { + evt, ok := event.(*events.CertificateCreate) + if ok { + certIssueEvent = evt + break + } + } + require.NotNil(t, certIssueEvent) + require.Empty(t, + cmp.Diff(certIssueEvent, &events.CertificateCreate{ + Metadata: events.Metadata{ + Type: libevents.CertificateCreateEvent, + Code: libevents.CertificateCreateCode, + }, + CertificateType: "user", + Identity: &events.Identity{ + User: "bot-test", + Roles: []string{"bot-test"}, + RouteToCluster: "localhost", + ClientIP: "127.0.0.1", + TeleportCluster: "localhost", + PrivateKeyPolicy: "none", + BotName: "test", + BotInstanceID: ident.BotInstanceID, + }, + }, + cmpopts.IgnoreFields(events.Metadata{}, "Time"), + cmpopts.IgnoreFields(events.Identity{}, "Logins", "Expires"), + cmpopts.EquateEmpty(), + ), + ) } // TestRegisterBotCertificateGenerationStolen simulates a stolen renewable diff --git a/lib/auth/integration/integrationv1/awsoidc.go b/lib/auth/integration/integrationv1/awsoidc.go index ab3a0277c182f..ad74145be366f 100644 --- a/lib/auth/integration/integrationv1/awsoidc.go +++ b/lib/auth/integration/integrationv1/awsoidc.go @@ -716,3 +716,45 @@ func (s *AWSOIDCService) ListSubnets(ctx context.Context, req *integrationpb.Lis NextToken: resp.NextToken, }, nil } + +// ListVPCs returns a list of AWS VPCs. +func (s *AWSOIDCService) ListVPCs(ctx context.Context, req *integrationpb.ListVPCsRequest) (*integrationpb.ListVPCsResponse, error) { + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.CheckAccessToKind(types.KindIntegration, types.VerbUse); err != nil { + return nil, trace.Wrap(err) + } + + awsClientReq, err := s.awsClientReq(ctx, req.Integration, req.Region) + if err != nil { + return nil, trace.Wrap(err) + } + + awsClient, err := awsoidc.NewListVPCsClient(ctx, awsClientReq) + if err != nil { + return nil, trace.Wrap(err) + } + + resp, err := awsoidc.ListVPCs(ctx, awsClient, awsoidc.ListVPCsRequest{ + NextToken: req.NextToken, + }) + if err != nil { + return nil, trace.Wrap(err) + } + + vpcs := make([]*integrationpb.VPC, 0, len(resp.VPCs)) + for _, s := range resp.VPCs { + vpcs = append(vpcs, &integrationpb.VPC{ + Name: s.Name, + Id: s.ID, + }) + } + + return &integrationpb.ListVPCsResponse{ + Vpcs: vpcs, + NextToken: resp.NextToken, + }, nil +} diff --git a/lib/auth/integration/integrationv1/awsoidc_test.go b/lib/auth/integration/integrationv1/awsoidc_test.go index e86ad969729c0..66d53fd02e74f 100644 --- a/lib/auth/integration/integrationv1/awsoidc_test.go +++ b/lib/auth/integration/integrationv1/awsoidc_test.go @@ -213,7 +213,7 @@ func TestConvertSecurityGroupRulesToProto(t *testing.T) { } } -func TestListEICE(t *testing.T) { +func TestRBAC(t *testing.T) { t.Parallel() clusterName := "test-cluster" @@ -241,141 +241,10 @@ func TestListEICE(t *testing.T) { }) require.NoError(t, err) - t.Run("fails when user doesn't have access to integration.use", func(t *testing.T) { - role := types.RoleSpecV6{ - Allow: types.RoleConditions{Rules: []types.Rule{{ - Resources: []string{types.KindIntegration}, - Verbs: []string{types.VerbRead}, - }}}, - } - - userCtx := authorizerForDummyUser(t, ctx, role, localClient) - - _, err = awsoidService.ListEICE(userCtx, &integrationv1.ListEICERequest{ - Integration: integrationName, - Region: "my-region", - VpcIds: []string{"vpc-123"}, - NextToken: "", - }) - require.True(t, trace.IsAccessDenied(err), "expected AccessDenied error, but got %T", err) - }) - t.Run("calls awsoidc package when user has access to integration.use/read", func(t *testing.T) { - role := types.RoleSpecV6{ - Allow: types.RoleConditions{Rules: []types.Rule{{ - Resources: []string{types.KindIntegration}, - Verbs: []string{types.VerbRead, types.VerbUse}, - }}}, - } - - userCtx := authorizerForDummyUser(t, ctx, role, localClient) - - _, err = awsoidService.ListEICE(userCtx, &integrationv1.ListEICERequest{ - Integration: integrationName, - Region: "", - VpcIds: []string{"vpc-123"}, - NextToken: "", - }) - require.True(t, trace.IsBadParameter(err), "expected BadParameter error, but got %T", err) - }) -} - -func TestListDatabases(t *testing.T) { - t.Parallel() - - clusterName := "test-cluster" - proxyPublicAddr := "127.0.0.1.nip.io" - integrationName := "my-awsoidc-integration" - ig, err := types.NewIntegrationAWSOIDC( - types.Metadata{Name: integrationName}, - &types.AWSOIDCIntegrationSpecV1{ - RoleARN: "arn:aws:iam::123456789012:role/OpsTeam", - }, - ) - require.NoError(t, err) - - ca := newCertAuthority(t, types.HostCA, clusterName) - ctx, localClient, integrationSvc := initSvc(t, ca, clusterName, proxyPublicAddr) - - _, err = localClient.CreateIntegration(ctx, ig) - require.NoError(t, err) - - awsSvc, err := NewAWSOIDCService(&AWSOIDCServiceConfig{ - IntegrationService: integrationSvc, - Authorizer: integrationSvc.authorizer, - ProxyPublicAddrGetter: func() string { return "128.0.0.1" }, - Cache: &mockCache{}, - }) - require.NoError(t, err) - - role := types.RoleSpecV6{ - Allow: types.RoleConditions{Rules: []types.Rule{{ - Resources: []string{types.KindIntegration}, - Verbs: []string{types.VerbRead}, - }}}, + type endpointSubtest struct { + name string + fn func() error } - - t.Run("fails when user doesn't have access to integration.use", func(t *testing.T) { - userCtx := authorizerForDummyUser(t, ctx, role, localClient) - _, err = awsSvc.ListDatabases(userCtx, &integrationv1.ListDatabasesRequest{ - Integration: integrationName, - Region: "", - RdsType: "", - Engines: []string{}, - NextToken: "", - VpcId: "vpc-123", - }) - require.True(t, trace.IsAccessDenied(err), "expected AccessDenied error, but got %T", err) - }) - t.Run("calls awsoidc package when user has access to integration.use/read", func(t *testing.T) { - role := types.RoleSpecV6{ - Allow: types.RoleConditions{Rules: []types.Rule{{ - Resources: []string{types.KindIntegration}, - Verbs: []string{types.VerbRead, types.VerbUse}, - }}}, - } - - userCtx := authorizerForDummyUser(t, ctx, role, localClient) - - _, err = awsSvc.ListDatabases(userCtx, &integrationv1.ListDatabasesRequest{ - Integration: integrationName, - Region: "", - RdsType: "", - Engines: []string{}, - NextToken: "", - VpcId: "vpc-123", - }) - require.True(t, trace.IsBadParameter(err), "expected BadParameter error, but got %T", err) - }) -} - -func TestEnrollEKSClusters(t *testing.T) { - t.Parallel() - - clusterName := "test-cluster" - proxyPublicAddr := "127.0.0.1" - integrationName := "my-awsoidc-integration" - ig, err := types.NewIntegrationAWSOIDC( - types.Metadata{Name: integrationName}, - &types.AWSOIDCIntegrationSpecV1{ - RoleARN: "arn:aws:iam::123456789012:role/OpsTeam", - }, - ) - require.NoError(t, err) - - ca := newCertAuthority(t, types.HostCA, clusterName) - ctx, localClient, resourceSvc := initSvc(t, ca, clusterName, proxyPublicAddr) - - _, err = localClient.CreateIntegration(ctx, ig) - require.NoError(t, err) - - awsoidService, err := NewAWSOIDCService(&AWSOIDCServiceConfig{ - IntegrationService: resourceSvc, - Authorizer: resourceSvc.authorizer, - ProxyPublicAddrGetter: func() string { return "128.0.0.1" }, - Cache: &mockCache{}, - }) - require.NoError(t, err) - t.Run("fails when user doesn't have access to integration.use", func(t *testing.T) { role := types.RoleSpecV6{ Allow: types.RoleConditions{Rules: []types.Rule{{ @@ -386,77 +255,84 @@ func TestEnrollEKSClusters(t *testing.T) { userCtx := authorizerForDummyUser(t, ctx, role, localClient) - _, err = awsoidService.EnrollEKSClusters(userCtx, &integrationv1.EnrollEKSClustersRequest{ - Integration: integrationName, - Region: "my-region", - EksClusterNames: []string{"EKS1"}, - AgentVersion: "10.0.0", - }) - require.True(t, trace.IsAccessDenied(err), "expected AccessDenied error, but got %T", err) - }) - t.Run("calls awsoidc package when user has access to integration.use/read", func(t *testing.T) { - role := types.RoleSpecV6{ - Allow: types.RoleConditions{Rules: []types.Rule{{ - Resources: []string{types.KindIntegration}, - Verbs: []string{types.VerbRead, types.VerbUse}, - }}}, + for _, tt := range []endpointSubtest{ + { + name: "ListEICE", + fn: func() error { + _, err := awsoidService.ListEICE(userCtx, &integrationv1.ListEICERequest{ + Integration: integrationName, + Region: "my-region", + VpcIds: []string{"vpc-123"}, + NextToken: "", + }) + return err + }, + }, + { + name: "ListDatabases", + fn: func() error { + _, err := awsoidService.ListDatabases(userCtx, &integrationv1.ListDatabasesRequest{ + Integration: integrationName, + Region: "", + RdsType: "", + Engines: []string{}, + NextToken: "", + VpcId: "vpc-123", + }) + return err + }, + }, + { + name: "EnrollEKSClusters", + fn: func() error { + _, err := awsoidService.EnrollEKSClusters(userCtx, &integrationv1.EnrollEKSClustersRequest{ + Integration: integrationName, + Region: "my-region", + EksClusterNames: []string{"EKS1"}, + AgentVersion: "10.0.0", + }) + return err + }, + }, + { + name: "DeployService", + fn: func() error { + _, err = awsoidService.DeployService(userCtx, &integrationv1.DeployServiceRequest{ + Integration: integrationName, + Region: "my-region", + }) + return err + }, + }, + { + name: "ListSubnets", + fn: func() error { + _, err := awsoidService.ListSubnets(userCtx, &integrationv1.ListSubnetsRequest{ + Integration: integrationName, + Region: "my-region", + VpcId: "vpc-1", + }) + return err + }, + }, + { + name: "ListVPCs", + fn: func() error { + _, err := awsoidService.ListVPCs(userCtx, &integrationv1.ListVPCsRequest{ + Integration: integrationName, + Region: "my-region", + }) + return err + }, + }, + } { + t.Run(tt.name, func(t *testing.T) { + err := tt.fn() + require.True(t, trace.IsAccessDenied(err), "expected AccessDenied error, but got %T", err) + }) } - - userCtx := authorizerForDummyUser(t, ctx, role, localClient) - - _, err := awsoidService.EnrollEKSClusters(userCtx, &integrationv1.EnrollEKSClustersRequest{ - Integration: integrationName, - Region: "my-region", - EksClusterNames: []string{"EKS1"}, - }) - require.True(t, trace.IsBadParameter(err), "expected BadParameter error, but got %T", err) }) -} -func TestDeployService(t *testing.T) { - t.Parallel() - - clusterName := "test-cluster" - proxyPublicAddr := "127.0.0.1.nip.io" - integrationName := "my-awsoidc-integration" - ig, err := types.NewIntegrationAWSOIDC( - types.Metadata{Name: integrationName}, - &types.AWSOIDCIntegrationSpecV1{ - RoleARN: "arn:aws:iam::123456789012:role/OpsTeam", - }, - ) - require.NoError(t, err) - - ca := newCertAuthority(t, types.HostCA, clusterName) - ctx, localClient, resourceSvc := initSvc(t, ca, clusterName, proxyPublicAddr) - - _, err = localClient.CreateIntegration(ctx, ig) - require.NoError(t, err) - - awsoidService, err := NewAWSOIDCService(&AWSOIDCServiceConfig{ - IntegrationService: resourceSvc, - Authorizer: resourceSvc.authorizer, - ProxyPublicAddrGetter: func() string { return "128.0.0.1" }, - Cache: &mockCache{}, - }) - require.NoError(t, err) - - t.Run("fails when user doesn't have access to integration.use", func(t *testing.T) { - role := types.RoleSpecV6{ - Allow: types.RoleConditions{Rules: []types.Rule{{ - Resources: []string{types.KindIntegration}, - Verbs: []string{types.VerbRead}, - }}}, - } - - userCtx := authorizerForDummyUser(t, ctx, role, localClient) - - _, err = awsoidService.DeployService(userCtx, &integrationv1.DeployServiceRequest{ - Integration: integrationName, - Region: "my-region", - }) - require.True(t, trace.IsAccessDenied(err), "expected AccessDenied error, but got %T", err) - }) t.Run("calls awsoidc package when user has access to integration.use/read", func(t *testing.T) { role := types.RoleSpecV6{ Allow: types.RoleConditions{Rules: []types.Rule{{ @@ -467,74 +343,81 @@ func TestDeployService(t *testing.T) { userCtx := authorizerForDummyUser(t, ctx, role, localClient) - _, err = awsoidService.DeployService(userCtx, &integrationv1.DeployServiceRequest{ - Integration: integrationName, - Region: "my-region", - }) - require.True(t, trace.IsBadParameter(err), "expected BadParameter error, but got %T", err) - }) -} - -func TestListSubnets(t *testing.T) { - t.Parallel() - - clusterName := "test-cluster" - proxyPublicAddr := "127.0.0.1.nip.io" - integrationName := "my-awsoidc-integration" - ig, err := types.NewIntegrationAWSOIDC( - types.Metadata{Name: integrationName}, - &types.AWSOIDCIntegrationSpecV1{ - RoleARN: "arn:aws:iam::123456789012:role/OpsTeam", - }, - ) - require.NoError(t, err) - - ca := newCertAuthority(t, types.HostCA, clusterName) - ctx, localClient, resourceSvc := initSvc(t, ca, clusterName, proxyPublicAddr) - - _, err = localClient.CreateIntegration(ctx, ig) - require.NoError(t, err) - - awsoidService, err := NewAWSOIDCService(&AWSOIDCServiceConfig{ - IntegrationService: resourceSvc, - Authorizer: resourceSvc.authorizer, - ProxyPublicAddrGetter: func() string { return "128.0.0.1" }, - Cache: &mockCache{}, - }) - require.NoError(t, err) - - t.Run("fails when user doesn't have access to integration.use", func(t *testing.T) { - role := types.RoleSpecV6{ - Allow: types.RoleConditions{Rules: []types.Rule{{ - Resources: []string{types.KindIntegration}, - Verbs: []string{types.VerbRead}, - }}}, - } - - userCtx := authorizerForDummyUser(t, ctx, role, localClient) - - _, err = awsoidService.ListSubnets(userCtx, &integrationv1.ListSubnetsRequest{ - Integration: integrationName, - Region: "my-region", - VpcId: "vpc-1", - }) - require.True(t, trace.IsAccessDenied(err), "expected AccessDenied error, but got %T", err) - }) - t.Run("calls awsoidc package when user has access to integration.use/read", func(t *testing.T) { - role := types.RoleSpecV6{ - Allow: types.RoleConditions{Rules: []types.Rule{{ - Resources: []string{types.KindIntegration}, - Verbs: []string{types.VerbRead, types.VerbUse}, - }}}, + for _, tt := range []endpointSubtest{ + { + name: "ListEICE", + fn: func() error { + _, err := awsoidService.ListEICE(userCtx, &integrationv1.ListEICERequest{ + Integration: integrationName, + Region: "my-region", + VpcIds: []string{"vpc-123"}, + NextToken: "", + }) + return err + }, + }, + { + name: "ListDatabases", + fn: func() error { + _, err := awsoidService.ListDatabases(userCtx, &integrationv1.ListDatabasesRequest{ + Integration: integrationName, + Region: "", + RdsType: "", + Engines: []string{}, + NextToken: "", + VpcId: "vpc-123", + }) + return err + }, + }, + { + name: "EnrollEKSClusters", + fn: func() error { + _, err := awsoidService.EnrollEKSClusters(userCtx, &integrationv1.EnrollEKSClustersRequest{ + Integration: integrationName, + Region: "my-region", + EksClusterNames: []string{"EKS1"}, + AgentVersion: "10.0.0", + }) + return err + }, + }, + { + name: "DeployService", + fn: func() error { + _, err = awsoidService.DeployService(userCtx, &integrationv1.DeployServiceRequest{ + Integration: integrationName, + Region: "my-region", + }) + return err + }, + }, + { + name: "ListSubnets", + fn: func() error { + _, err := awsoidService.ListSubnets(userCtx, &integrationv1.ListSubnetsRequest{ + Integration: integrationName, + Region: "my-region", + VpcId: "vpc-1", + }) + return err + }, + }, + { + name: "ListVPCs", + fn: func() error { + _, err := awsoidService.ListVPCs(userCtx, &integrationv1.ListVPCsRequest{ + Integration: integrationName, + Region: "my-region", + }) + return err + }, + }, + } { + t.Run(tt.name, func(t *testing.T) { + err := tt.fn() + require.True(t, trace.IsBadParameter(err), "expected BadParameter error, but got %T", err) + }) } - - userCtx := authorizerForDummyUser(t, ctx, role, localClient) - - _, err = awsoidService.ListSubnets(userCtx, &integrationv1.ListSubnetsRequest{ - Integration: integrationName, - Region: "my-region", - VpcId: "", - }) - require.True(t, trace.IsBadParameter(err), "expected BadParameter error, but got %T", err) }) } diff --git a/lib/auth/join.go b/lib/auth/join.go index fa251c6b89e25..bf1ed3219fdf4 100644 --- a/lib/auth/join.go +++ b/lib/auth/join.go @@ -398,14 +398,22 @@ func (a *Server) generateCertsBot( } } - certs, err := a.generateInitialBotCerts( - ctx, botName, machineidv1.BotResourceName(botName), req.RemoteAddr, - req.PublicSSHKey, req.PublicTLSKey, expires, renewable, auth, + certs, botInstanceID, err := a.generateInitialBotCerts( + ctx, + botName, + machineidv1.BotResourceName(botName), + req.RemoteAddr, + req.PublicSSHKey, + req.PublicTLSKey, + expires, + renewable, + auth, req.BotInstanceID, ) if err != nil { return nil, trace.Wrap(err) } + joinEvent.BotInstanceID = botInstanceID if shouldDeleteToken { // delete ephemeral bot join tokens so they can't be re-used @@ -417,8 +425,7 @@ func (a *Server) generateCertsBot( } // Emit audit event for bot join. - log.Infof("Bot %q has joined the cluster.", botName) - + log.Infof("Bot %q (instance: %s) has joined the cluster.", botName, botInstanceID) if err := a.emitter.EmitAuditEvent(ctx, joinEvent); err != nil { log.WithError(err).Warn("Failed to emit bot join event.") } diff --git a/lib/auth/middleware.go b/lib/auth/middleware.go index 680c843be7a04..2a27513193061 100644 --- a/lib/auth/middleware.go +++ b/lib/auth/middleware.go @@ -111,16 +111,6 @@ func (c *TLSServerConfig) CheckAndSetDefaults() error { if c.TLS == nil { return trace.BadParameter("missing parameter TLS") } - c.TLS.ClientAuth = tls.VerifyClientCertIfGiven - if c.TLS.ClientCAs == nil { - return trace.BadParameter("missing parameter TLS.ClientCAs") - } - if c.TLS.RootCAs == nil { - return trace.BadParameter("missing parameter TLS.RootCAs") - } - if len(c.TLS.Certificates) == 0 { - return trace.BadParameter("missing parameter TLS.Certificates") - } if c.GetClientCertificate == nil { return trace.BadParameter("missing parameter GetClientCertificate") } @@ -210,9 +200,6 @@ func NewTLSServer(ctx context.Context, cfg TLSServerConfig) (*TLSServer, error) authMiddleware.Wrap(apiServer) // Wrap sets the next middleware in chain to the authMiddleware limiter.WrapHandle(authMiddleware) - // force client auth if given - cfg.TLS.ClientAuth = tls.VerifyClientCertIfGiven - cfg.TLS.NextProtos = []string{http2.NextProtoTLS} securityHeaderHandler := httplib.MakeSecurityHeaderHandler(limiter) tracingHandler := httplib.MakeTracingHandler(securityHeaderHandler, teleport.ComponentAuth) @@ -234,8 +221,13 @@ func NewTLSServer(ctx context.Context, cfg TLSServerConfig) (*TLSServer, error) }), } + tlsConfig := cfg.TLS.Clone() + // force client auth if given + tlsConfig.ClientAuth = tls.VerifyClientCertIfGiven + tlsConfig.NextProtos = []string{http2.NextProtoTLS} + server.clientTLSConfigGenerator, err = NewClientTLSConfigGenerator(ClientTLSConfigGeneratorConfig{ - TLS: server.cfg.TLS.Clone(), + TLS: tlsConfig, ClusterName: localClusterName.GetClusterName(), PermitRemoteClusters: true, AccessPoint: server.cfg.AccessPoint, @@ -244,10 +236,10 @@ func NewTLSServer(ctx context.Context, cfg TLSServerConfig) (*TLSServer, error) return nil, trace.Wrap(err) } - server.cfg.TLS.GetConfigForClient = server.clientTLSConfigGenerator.GetConfigForClient + tlsConfig.GetConfigForClient = server.clientTLSConfigGenerator.GetConfigForClient server.grpcServer, err = NewGRPCServer(GRPCServerConfig{ - TLS: server.cfg.TLS, + TLS: tlsConfig, Middleware: authMiddleware, APIConfig: cfg.APIConfig, UnaryInterceptors: authMiddleware.UnaryInterceptors(), @@ -258,7 +250,7 @@ func NewTLSServer(ctx context.Context, cfg TLSServerConfig) (*TLSServer, error) } server.mux, err = multiplexer.NewTLSListener(multiplexer.TLSListenerConfig{ - Listener: tls.NewListener(cfg.Listener, server.cfg.TLS), + Listener: tls.NewListener(cfg.Listener, tlsConfig), ID: cfg.ID, }) if err != nil { diff --git a/lib/auth/saml.go b/lib/auth/saml.go index ffed49e9bdbf0..d9ea620b3cf25 100644 --- a/lib/auth/saml.go +++ b/lib/auth/saml.go @@ -59,6 +59,15 @@ func (a *Server) UpsertSAMLConnector(ctx context.Context, connector types.SAMLCo return nil, trace.Wrap(err) } + // If someone is applying a SAML Connector obtained with `tctl get` without secrets, the signing key pair is + // not empty (cert is set) but the private key is missing. Such a SAML resource is invalid and not usable. + if connector.GetSigningKeyPair().PrivateKey == "" { + err := services.FillSAMLSigningKeyFromExisting(ctx, connector, a.Services) + if err != nil { + return nil, trace.Wrap(err) + } + } + upserted, err := a.Services.UpsertSAMLConnector(ctx, connector) if err != nil { return nil, trace.Wrap(err) @@ -94,6 +103,17 @@ func (a *Server) UpdateSAMLConnector(ctx context.Context, connector types.SAMLCo return nil, trace.Wrap(err) } + // If someone is applying a SAML Connector obtained with `tctl get` without secrets, the signing key pair is + // not empty (cert is set) but the private key is missing. In this case we want to look up the existing SAML + // connector and populate the singing key from it if it's the same certificate. This avoids accidentally clearing + // the private key and creating an unusable connector. + if connector.GetSigningKeyPair().PrivateKey == "" { + err := services.FillSAMLSigningKeyFromExisting(ctx, connector, a.Services) + if err != nil { + return nil, trace.Wrap(err) + } + } + updated, err := a.Services.UpdateSAMLConnector(ctx, connector) if err != nil { return nil, trace.Wrap(err) @@ -129,6 +149,13 @@ func (a *Server) CreateSAMLConnector(ctx context.Context, connector types.SAMLCo return nil, trace.Wrap(err) } + // If someone is applying a SAML Connector obtained with `tctl get` without secrets, the signing key pair is + // not empty (cert is set) but the private key is missing. This SAML Connector is invalid, we must reject it + // with an actionable message. + if connector.GetSigningKeyPair().PrivateKey == "" { + return nil, trace.BadParameter("Missing private key for signing connector. " + services.ErrMsgHowToFixMissingPrivateKey) + } + created, err := a.Services.CreateSAMLConnector(ctx, connector) if err != nil { return nil, trace.Wrap(err) diff --git a/lib/backend/buffer.go b/lib/backend/buffer.go index 65d601b4e555c..2d52fe1bdfb8c 100644 --- a/lib/backend/buffer.go +++ b/lib/backend/buffer.go @@ -36,9 +36,10 @@ import ( ) type bufferConfig struct { - gracePeriod time.Duration - capacity int - clock clockwork.Clock + gracePeriod time.Duration + creationGracePeriod time.Duration + capacity int + clock clockwork.Clock } type BufferOption func(*bufferConfig) @@ -61,6 +62,16 @@ func BacklogGracePeriod(d time.Duration) BufferOption { } } +// CreationGracePeriod sets the amount of time delay after watcher creation before +// it will be considered for removal due to backlog. +func CreationGracePeriod(d time.Duration) BufferOption { + return func(cfg *bufferConfig) { + if d > 0 { + cfg.creationGracePeriod = d + } + } +} + // BufferClock sets a custom clock for the buffer (used in tests). func BufferClock(c clockwork.Clock) BufferOption { return func(cfg *bufferConfig) { @@ -83,9 +94,10 @@ type CircularBuffer struct { // NewCircularBuffer returns a new uninitialized instance of circular buffer. func NewCircularBuffer(opts ...BufferOption) *CircularBuffer { cfg := bufferConfig{ - gracePeriod: DefaultBacklogGracePeriod, - capacity: DefaultBufferCapacity, - clock: clockwork.NewRealClock(), + gracePeriod: DefaultBacklogGracePeriod, + creationGracePeriod: DefaultCreationGracePeriod, + capacity: DefaultBufferCapacity, + clock: clockwork.NewRealClock(), } for _, opt := range opts { opt(&cfg) @@ -258,6 +270,7 @@ func (c *CircularBuffer) NewWatcher(ctx context.Context, watch Watch) (Watcher, buffer: c, Watch: watch, eventsC: make(chan Event, watch.QueueSize), + created: c.cfg.clock.Now(), ctx: closeCtx, cancel: cancel, capacity: watch.QueueSize, @@ -297,6 +310,7 @@ type BufferWatcher struct { bmu sync.Mutex backlog []Event backlogSince time.Time + created time.Time ctx context.Context cancel context.CancelFunc @@ -352,7 +366,7 @@ func (w *BufferWatcher) emit(e Event) (ok bool) { defer w.bmu.Unlock() if !w.flushBacklog() { - if w.buffer.cfg.clock.Now().After(w.backlogSince.Add(w.buffer.cfg.gracePeriod)) { + if now := w.buffer.cfg.clock.Now(); now.After(w.backlogSince.Add(w.buffer.cfg.gracePeriod)) && now.After(w.created.Add(w.buffer.cfg.creationGracePeriod)) { // backlog has existed for longer than grace period, // this watcher needs to be removed. return false diff --git a/lib/backend/buffer_test.go b/lib/backend/buffer_test.go index 6070d9ac24487..91cd1faf9e172 100644 --- a/lib/backend/buffer_test.go +++ b/lib/backend/buffer_test.go @@ -83,6 +83,7 @@ func TestWatcherCapacity(t *testing.T) { BufferCapacity(1), BufferClock(clock), BacklogGracePeriod(gracePeriod), + CreationGracePeriod(time.Nanosecond), ) defer b.Close() b.SetInit() @@ -145,6 +146,71 @@ func TestWatcherCapacity(t *testing.T) { } } +func TestWatcherCreationGracePeriod(t *testing.T) { + const backlogGracePeriod = time.Second + const creationGracePeriod = backlogGracePeriod * 3 + const queueSize = 1 + clock := clockwork.NewFakeClock() + + ctx := context.Background() + b := NewCircularBuffer( + BufferCapacity(1), + BufferClock(clock), + BacklogGracePeriod(backlogGracePeriod), + CreationGracePeriod(creationGracePeriod), + ) + defer b.Close() + b.SetInit() + + w, err := b.NewWatcher(ctx, Watch{ + QueueSize: queueSize, + }) + require.NoError(t, err) + defer w.Close() + + select { + case e := <-w.Events(): + require.Equal(t, types.OpInit, e.Type) + default: + t.Fatalf("Expected immediate OpInit.") + } + + // emit enough events to create a backlog + for i := 0; i < queueSize*2; i++ { + b.Emit(Event{Item: Item{Key: []byte{Separator}}}) + } + + select { + case <-w.Done(): + t.Fatal("watcher closed unexpectedly") + default: + } + + // sanity-check + require.Greater(t, creationGracePeriod, backlogGracePeriod*2) + + // advance well past the backlog grace period, but not past the creation grace period + clock.Advance(backlogGracePeriod * 2) + + b.Emit(Event{Item: Item{Key: []byte{Separator}}}) + + select { + case <-w.Done(): + t.Fatal("watcher closed unexpectedly") + default: + } + + // advance well past creation grace period + clock.Advance(creationGracePeriod) + + b.Emit(Event{Item: Item{Key: []byte{Separator}}}) + select { + case <-w.Done(): + default: + t.Fatal("watcher did not close after creation grace period exceeded") + } +} + // TestWatcherClose makes sure that closed watcher // will be removed func TestWatcherClose(t *testing.T) { diff --git a/lib/backend/defaults.go b/lib/backend/defaults.go index 6ebbb000f98a7..d2e16ec21b8ba 100644 --- a/lib/backend/defaults.go +++ b/lib/backend/defaults.go @@ -32,6 +32,12 @@ const ( // (e.g. heartbeats) are be created. If a watcher can't catch up in under a minute, // it probably won't catch up. DefaultBacklogGracePeriod = time.Second * 59 + // DefaultCreationGracePeriod is the default amount of time time that the circular buffer + // will wait before enforcing the backlog grace period. This is intended to give downstream + // caches time to initialize before they start receiving events. Without this, large caches + // may be unable to successfully initialize even if they would otherwise be able to keep up + // with the event stream once established. + DefaultCreationGracePeriod = DefaultBacklogGracePeriod * 3 // DefaultPollStreamPeriod is a default event poll stream period DefaultPollStreamPeriod = time.Second // DefaultEventsTTL is a default events TTL period diff --git a/lib/backend/report.go b/lib/backend/report.go index 611db8bc7f4f1..1455d7dc6ff31 100644 --- a/lib/backend/report.go +++ b/lib/backend/report.go @@ -21,6 +21,7 @@ package backend import ( "context" "errors" + "log/slog" "math" "slices" "strings" @@ -33,11 +34,13 @@ import ( log "github.com/sirupsen/logrus" "go.opentelemetry.io/otel/attribute" oteltrace "go.opentelemetry.io/otel/trace" + "golang.org/x/time/rate" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/observability/metrics" "github.com/gravitational/teleport/lib/observability/tracing" + logutils "github.com/gravitational/teleport/lib/utils/log" ) const reporterDefaultCacheSize = 1000 @@ -88,6 +91,8 @@ type Reporter struct { // This will keep an upper limit on our memory usage while still always // reporting the most active keys. topRequestsCache *lru.Cache[topRequestsCacheKey, struct{}] + + slowRangeLogLimiter *rate.Limiter } // NewReporter returns a new Reporter. @@ -109,8 +114,9 @@ func NewReporter(cfg ReporterConfig) (*Reporter, error) { return nil, trace.Wrap(err) } r := &Reporter{ - ReporterConfig: cfg, - topRequestsCache: cache, + ReporterConfig: cfg, + topRequestsCache: cache, + slowRangeLogLimiter: rate.NewLimiter(rate.Every(time.Minute), 12), } return r, nil } @@ -142,6 +148,12 @@ func (s *Reporter) GetRange(ctx context.Context, startKey []byte, endKey []byte, reads.WithLabelValues(s.Component).Add(float64(len(res.Items))) } s.trackRequest(types.OpGet, startKey, endKey) + end := s.Clock().Now() + if d := end.Sub(start); d > time.Second*3 { + if s.slowRangeLogLimiter.AllowN(end, 1) { + slog.WarnContext(ctx, "slow GetRange request", "start_key", string(startKey), "end_key", string(endKey), "limit", limit, "duration", logutils.StringerAttr(d)) + } + } return res, err } diff --git a/lib/cache/cache.go b/lib/cache/cache.go index c15afe3c77de6..a744ea0df5f25 100644 --- a/lib/cache/cache.go +++ b/lib/cache/cache.go @@ -1261,6 +1261,8 @@ func (c *Cache) fetchAndWatch(ctx context.Context, retry retryutils.Retry, timer return trace.ConnectionProblem(nil, "timeout waiting for watcher init") } + fetchAndApplyStart := time.Now() + confirmedKindsMap := make(map[resourceKind]types.WatchKind, len(confirmedKinds)) for _, kind := range confirmedKinds { confirmedKindsMap[resourceKind{kind: kind.Kind, subkind: kind.SubKind}] = kind @@ -1324,6 +1326,19 @@ func (c *Cache) fetchAndWatch(ctx context.Context, retry retryutils.Retry, timer c.notify(c.ctx, Event{Type: WatcherStarted}) + fetchAndApplyDuration := time.Since(fetchAndApplyStart) + if fetchAndApplyDuration > time.Second*20 { + c.Logger.WithFields(log.Fields{ + "cache_target": c.Config.target, + "duration": fetchAndApplyDuration.String(), + }).Warn("slow fetch and apply") + } else { + c.Logger.WithFields(log.Fields{ + "cache_target": c.Config.target, + "duration": fetchAndApplyDuration.String(), + }).Debug("fetch and apply") + } + var lastStalenessWarning time.Time var staleEventCount int for { @@ -1408,7 +1423,13 @@ func (c *Cache) fetchAndWatch(ctx context.Context, retry retryutils.Retry, timer // cannot run concurrently with event processing. func (c *Cache) performRelativeNodeExpiry(ctx context.Context) error { // TODO(fspmarshall): Start using dynamic value once it is implemented. - gracePeriod := apidefaults.ServerAnnounceTTL + + // because event streams are not necessarily ordered across keys expiring on the + // server announce TTL may sometimes generate false positives. Using the watcher + // creation grace period as our safety buffer is mostly an arbitrary choice, but + // since it approximates our expected worst-case staleness of the event stream its + // a fairly reasonable one. + gracePeriod := apidefaults.ServerAnnounceTTL + backend.DefaultCreationGracePeriod // latestExp will be the value that we choose to consider the most recent "expired" // timestamp. This will either end up being the most recently seen node expiry, or diff --git a/lib/cache/cache_test.go b/lib/cache/cache_test.go index 64e4e60b8fd78..981eb343dc13a 100644 --- a/lib/cache/cache_test.go +++ b/lib/cache/cache_test.go @@ -2912,7 +2912,7 @@ func TestRelativeExpiryLimit(t *testing.T) { require.Len(t, nodes, nodeCount) clock.Advance(time.Hour * 24) - for expired := nodeCount - expiryLimit; expired > 0; expired -= expiryLimit { + for expired := nodeCount - expiryLimit; expired > expiryLimit; expired -= expiryLimit { // get rid of events that were emitted before clock advanced drainEvents(p.eventsC) // wait for next relative expiry check to run diff --git a/lib/cloud/gcp/sql.go b/lib/cloud/gcp/sql.go index 65350e982347e..109d862889125 100644 --- a/lib/cloud/gcp/sql.go +++ b/lib/cloud/gcp/sql.go @@ -34,7 +34,6 @@ import ( "github.com/gravitational/teleport/api/constants" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/utils/keys" - "github.com/gravitational/teleport/lib/tlsca" ) // SQLAdminClient defines an interface providing access to the GCP Cloud SQL API. @@ -48,7 +47,7 @@ type SQLAdminClient interface { GetDatabaseInstance(ctx context.Context, db types.Database) (*sqladmin.DatabaseInstance, error) // GenerateEphemeralCert returns a new client certificate with RSA key for the // project/instance configured in a session. - GenerateEphemeralCert(ctx context.Context, db types.Database, identity tlsca.Identity) (*tls.Certificate, error) + GenerateEphemeralCert(ctx context.Context, db types.Database, certExpiry time.Time) (*tls.Certificate, error) } // NewGCPSQLAdminClient returns a GCPSQLAdminClient interface wrapping sqladmin.Service. @@ -103,7 +102,7 @@ func (g *gcpSQLAdminClient) GetDatabaseInstance(ctx context.Context, db types.Da // GenerateEphemeralCert returns a new client certificate with RSA key created // using the GenerateEphemeralCertRequest Cloud SQL API. Client certificates are // required when enabling SSL in Cloud SQL. -func (g *gcpSQLAdminClient) GenerateEphemeralCert(ctx context.Context, db types.Database, identity tlsca.Identity) (*tls.Certificate, error) { +func (g *gcpSQLAdminClient) GenerateEphemeralCert(ctx context.Context, db types.Database, certExpiry time.Time) (*tls.Certificate, error) { // TODO(jimbishopp): cache database certificates to avoid expensive generate // operation on each connection. @@ -121,7 +120,7 @@ func (g *gcpSQLAdminClient) GenerateEphemeralCert(ctx context.Context, db types. gcp := db.GetGCP() req := g.service.Connect.GenerateEphemeralCert(gcp.ProjectID, gcp.InstanceID, &sqladmin.GenerateEphemeralCertRequest{ PublicKey: string(pem.EncodeToMemory(&pem.Block{Bytes: pkix, Type: "RSA PUBLIC KEY"})), - ValidDuration: fmt.Sprintf("%ds", int(time.Until(identity.Expires).Seconds())), + ValidDuration: fmt.Sprintf("%ds", int(time.Until(certExpiry).Seconds())), }) resp, err := req.Context(ctx).Do() if err != nil { diff --git a/lib/cloud/mocks/gcp.go b/lib/cloud/mocks/gcp.go index 7e65d77f036c9..8cf686d3d90cb 100644 --- a/lib/cloud/mocks/gcp.go +++ b/lib/cloud/mocks/gcp.go @@ -30,7 +30,6 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/cloud/gcp" - "github.com/gravitational/teleport/lib/tlsca" ) var _ gcp.SQLAdminClient = (*GCPSQLAdminClientMock)(nil) @@ -60,7 +59,7 @@ func (g *GCPSQLAdminClientMock) GetDatabaseInstance(ctx context.Context, db type return g.DatabaseInstance, nil } -func (g *GCPSQLAdminClientMock) GenerateEphemeralCert(ctx context.Context, db types.Database, identity tlsca.Identity) (*tls.Certificate, error) { +func (g *GCPSQLAdminClientMock) GenerateEphemeralCert(ctx context.Context, db types.Database, certExpiry time.Time) (*tls.Certificate, error) { return g.EphemeralCert, nil } diff --git a/lib/config/configuration.go b/lib/config/configuration.go index 8a26e74b6933b..a3b821ce4449d 100644 --- a/lib/config/configuration.go +++ b/lib/config/configuration.go @@ -359,22 +359,6 @@ type IntegrationConfAWSOIDCIdP struct { // ProxyPublicURL is the IdP Issuer URL (Teleport Proxy Public Address). // Eg, https://.teleport.sh ProxyPublicURL string - - // S3BucketURI is the S3 URI which contains the bucket name and prefix for the issuer. - // Format: s3:/// - // Eg, s3://my-bucket/idp-teleport - // This is used in two places: - // - create openid configuration and jwks objects - // - set up the issuer - // The bucket must be public and will be created if it doesn't exist. - // - // If empty, the ProxyPublicAddress is used as issuer and no s3 objects are created. - S3BucketURI string - - // S3JWKSContentsB64 must contain the public keys for the Issuer. - // The contents must be Base64 encoded. - // Eg. base64(`{"keys":[{"kty":"RSA","alg":"RS256","n":"","e":"","use":"sig","kid":""}]}`) - S3JWKSContentsB64 string } // IntegrationConfListDatabasesIAM contains the arguments of diff --git a/lib/httplib/reverseproxy/rewriter.go b/lib/httplib/reverseproxy/rewriter.go index 507d2eecc0543..2a23dc272308d 100644 --- a/lib/httplib/reverseproxy/rewriter.go +++ b/lib/httplib/reverseproxy/rewriter.go @@ -126,9 +126,11 @@ func maybeSetXRealIP(req *http.Request) { // maybeSetForwarded sets X-Forwarded-* headers if it is not set to the // scheme of the request. func maybeSetForwarded(req *http.Request) { - // We need to delete the value because httputil.ReverseProxy - // appends to the existing value. - req.Header.Del(XForwardedFor) + // Set X-Forwarded-For since net/http/httputil.ReverseProxy won't + // do this when Rewrite is set. + if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { + req.Header.Set(XForwardedFor, clientIP) + } if req.Header.Get(XForwardedProto) != "" { return diff --git a/lib/httplib/reverseproxy/rewriter_test.go b/lib/httplib/reverseproxy/rewriter_test.go index b1da6c7241213..0656708523c4e 100644 --- a/lib/httplib/reverseproxy/rewriter_test.go +++ b/lib/httplib/reverseproxy/rewriter_test.go @@ -100,6 +100,7 @@ func TestRewriter(t *testing.T) { hostReq: "teleport.dev:3543", remoteAddr: "1.2.3.4:1234", expected: http.Header{ + XForwardedFor: []string{"1.2.3.4"}, XForwardedHost: []string{"teleport.dev:3543"}, XForwardedPort: []string{"3543"}, XForwardedProto: []string{"https"}, @@ -117,6 +118,7 @@ func TestRewriter(t *testing.T) { hostReq: "teleport.dev:3543", remoteAddr: "1.2.3.4:1234", expected: http.Header{ + XForwardedFor: []string{"1.2.3.4"}, XForwardedHost: []string{"teleport.dev:3543"}, XForwardedPort: []string{"3543"}, XForwardedProto: []string{"http"}, @@ -133,6 +135,7 @@ func TestRewriter(t *testing.T) { hostReq: "teleport.dev", remoteAddr: "1.2.3.4:1234", expected: http.Header{ + XForwardedFor: []string{"1.2.3.4"}, XForwardedHost: []string{"teleport.dev"}, XForwardedPort: []string{"80"}, XForwardedProto: []string{"http"}, @@ -141,9 +144,11 @@ func TestRewriter(t *testing.T) { }, }, } + rewriter := NewHeaderRewriter() // set hostname to make sure it's the same in all tests. rewriter.Hostname = hostname + for _, test := range testCases { test := test t.Run(test.desc, func(t *testing.T) { diff --git a/lib/integrations/awsoidc/deploydatabaseservice.go b/lib/integrations/awsoidc/deploydatabaseservice.go index f17552d62c79c..d6d35ef91585c 100644 --- a/lib/integrations/awsoidc/deploydatabaseservice.go +++ b/lib/integrations/awsoidc/deploydatabaseservice.go @@ -259,11 +259,9 @@ func DeployDatabaseService(ctx context.Context, clt DeployServiceClient, req Dep } } - clusterDashboardURL := fmt.Sprintf("https://%s.console.aws.amazon.com/ecs/v2/clusters/%s/services", req.Region, aws.ToString(cluster.ClusterName)) - return &DeployDatabaseServiceResponse{ ClusterARN: aws.ToString(cluster.ClusterArn), - ClusterDashboardURL: clusterDashboardURL, + ClusterDashboardURL: ecsClusterDashboardURL(req.Region, aws.ToString(cluster.ClusterName)), }, nil } @@ -276,3 +274,27 @@ func ecsTaskName(teleportClusterName, deploymentMode, vpcid string) string { func ecsServiceName(deploymentMode, vpcid string) string { return normalizeECSResourceName(fmt.Sprintf("%s-%s", deploymentMode, vpcid)) } + +// ecsClusterDashboardURL returns the ECS cluster dashboard URL for a given +// region and ECS cluster. +func ecsClusterDashboardURL(region, ecsClusterName string) string { + return fmt.Sprintf("https://%s.console.aws.amazon.com/ecs/v2/clusters/%s/services", region, ecsClusterName) +} + +// ECSDatabaseServiceDashboardURL returns the ECS service dashboard URL for +// a deployed database service. +func ECSDatabaseServiceDashboardURL(region, teleportClusterName, vpcID string) (string, error) { + if region == "" { + return "", trace.BadParameter("empty region") + } + if teleportClusterName == "" { + return "", trace.BadParameter("empty cluster name") + } + if vpcID == "" { + return "", trace.BadParameter("empty VPC ID") + } + ecsClusterName := normalizeECSClusterName(teleportClusterName) + ecsClusterDashboard := ecsClusterDashboardURL(region, ecsClusterName) + serviceName := ecsServiceName(DatabaseServiceDeploymentMode, vpcID) + return fmt.Sprintf("%s/%s", ecsClusterDashboard, serviceName), nil +} diff --git a/lib/integrations/awsoidc/deploydatabaseservice_test.go b/lib/integrations/awsoidc/deploydatabaseservice_test.go index a61309f9c8325..ec871eaf092ac 100644 --- a/lib/integrations/awsoidc/deploydatabaseservice_test.go +++ b/lib/integrations/awsoidc/deploydatabaseservice_test.go @@ -554,3 +554,56 @@ func TestDeployDatabaseService(t *testing.T) { require.Equal(t, "ARNcluster-name-teleport", resp.ClusterARN) }) } + +func TestECSDatabaseServiceDashboardURL(t *testing.T) { + tests := []struct { + name string + region string + teleportClusterName string + vpcID string + wantURL string + wantErrContains string + }{ + { + name: "valid params", + region: "us-west-1", + teleportClusterName: "foo.bar.com", + vpcID: "vpc-123", + wantURL: "https://us-west-1.console.aws.amazon.com/ecs/v2/clusters/foo_bar_com-teleport/services/database-service-vpc-123", + }, + { + name: "empty region is an error", + region: "", + teleportClusterName: "foo.bar.com", + vpcID: "vpc-123", + wantErrContains: "empty region", + }, + { + name: "empty cluster name is an error", + region: "us-west-1", + teleportClusterName: "", + vpcID: "vpc-123", + wantErrContains: "empty cluster name", + }, + { + name: "empty VPC ID is an error", + region: "us-west-1", + teleportClusterName: "foo.bar.com", + vpcID: "", + wantErrContains: "empty VPC", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ECSDatabaseServiceDashboardURL(tt.region, tt.teleportClusterName, tt.vpcID) + if tt.wantErrContains != "" { + require.Error(t, err) + require.ErrorContains(t, err, tt.wantErrContains) + return + } + require.NoError(t, err) + require.Equal(t, tt.wantURL, got) + }) + } +} diff --git a/lib/integrations/awsoidc/idp_iam_config.go b/lib/integrations/awsoidc/idp_iam_config.go index 3b22490029bd6..c1b2036a9639f 100644 --- a/lib/integrations/awsoidc/idp_iam_config.go +++ b/lib/integrations/awsoidc/idp_iam_config.go @@ -19,22 +19,14 @@ package awsoidc import ( - "bytes" "context" - "encoding/base64" - "encoding/json" - "fmt" "log/slog" "net/http" "net/url" - "path" - "strings" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/iam" - "github.com/aws/aws-sdk-go-v2/service/s3" - s3types "github.com/aws/aws-sdk-go-v2/service/s3/types" "github.com/aws/aws-sdk-go-v2/service/sts" "github.com/gravitational/trace" @@ -42,8 +34,6 @@ import ( awslib "github.com/gravitational/teleport/lib/cloud/aws" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/integrations/awsoidc/tags" - awsutil "github.com/gravitational/teleport/lib/utils/aws" - "github.com/gravitational/teleport/lib/utils/oidc" ) const ( @@ -67,33 +57,13 @@ type IdPIAMConfigureRequest struct { // ProxyPublicAddress is the URL to use as provider URL. // This must be a valid URL (ie, url.Parse'able) // Eg, https://.teleport.sh, https://proxy.example.org:443, https://teleport.ec2.aws:3080 - // Only one of ProxyPublicAddress or S3BucketLocation can be used. ProxyPublicAddress string - // S3BucketLocation is the S3 URI which contains the bucket name and prefix for the issuer. - // Format: s3:/// - // Eg, s3://my-bucket/idp-teleport - // This is used in two places: - // - create openid configuration and jwks objects - // - set up the issuer - // The bucket must be public and will be created if it doesn't exist. - // - // If empty, the ProxyPublicAddress is used as issuer and no s3 objects are created. - S3BucketLocation string - - // S3JWKSContentsB64 must contain the public keys for the Issuer. - // The contents must be Base64 encoded. - // Eg. base64(`{"keys":[{"kty":"RSA","alg":"RS256","n":"","e":"","use":"sig","kid":""}]}`) - S3JWKSContentsB64 string - s3Bucket string - s3BucketPrefix string - jwksFileContents []byte - // issuer is the above value but only contains the host. - // Eg, .teleport.sh, proxy.example.org, my-bucket.s3.amazonaws.com/my-prefix + // Eg, .teleport.sh, proxy.example.org issuer string // issuerURL is the full url for the issuer - // Eg, https://.teleport.sh, https://proxy.example.org, https://my-bucket.s3.amazonaws.com/my-prefix + // Eg, https://.teleport.sh, https://proxy.example.org issuerURL string // IntegrationRole is the Integration's AWS Role used to set up Teleport as an OIDC IdP. @@ -116,41 +86,19 @@ func (r *IdPIAMConfigureRequest) CheckAndSetDefaults() error { return trace.BadParameter("integration role is required") } - if (r.ProxyPublicAddress == "" && r.S3BucketLocation == "") || (r.ProxyPublicAddress != "" && r.S3BucketLocation != "") { - return trace.BadParameter("provide only one of --proxy-public-url or --s3-bucket-uri") + if r.ProxyPublicAddress == "" { + return trace.BadParameter("argument --proxy-public-url is required") } - if r.ProxyPublicAddress != "" { - issuerURL, err := url.Parse(r.ProxyPublicAddress) - if err != nil { - return trace.BadParameter("--proxy-public-url is not a valid url: %v", err) - } - r.issuer = issuerURL.Host - if issuerURL.Port() == "443" { - r.issuer = issuerURL.Hostname() - } - r.issuerURL = issuerURL.String() + issuerURL, err := url.Parse(r.ProxyPublicAddress) + if err != nil { + return trace.BadParameter("--proxy-public-url is not a valid url: %v", err) } - - if r.S3BucketLocation != "" { - s3BucketURL, err := url.Parse(r.S3BucketLocation) - if err != nil || s3BucketURL.Scheme != "s3" { - return trace.BadParameter("--s3-bucket-uri must be valid s3 uri (eg s3://bucket/prefix)") - } - r.s3Bucket = s3BucketURL.Host - r.s3BucketPrefix = strings.TrimPrefix(s3BucketURL.Path, "/") - - r.issuer = fmt.Sprintf("%s.s3.amazonaws.com/%s", r.s3Bucket, r.s3BucketPrefix) - r.issuerURL = "https://" + r.issuer - - if len(r.S3JWKSContentsB64) == 0 { - return trace.BadParameter("--s3-jwks-base64 is required.") - } - r.jwksFileContents, err = base64.StdEncoding.DecodeString(r.S3JWKSContentsB64) - if err != nil { - return trace.BadParameter("--s3-jwks-base64 is invalid: %v", err) - } + r.issuer = issuerURL.Host + if issuerURL.Port() == "443" { + r.issuer = issuerURL.Hostname() } + r.issuerURL = issuerURL.String() r.ownershipTags = tags.DefaultResourceCreationTags(r.Cluster, r.IntegrationName) @@ -160,17 +108,6 @@ func (r *IdPIAMConfigureRequest) CheckAndSetDefaults() error { // IdPIAMConfigureClient describes the required methods to create the AWS OIDC IdP and a Role that trusts that identity provider. // There is no guarantee that the client is thread safe. type IdPIAMConfigureClient interface { - // SetAWSRegion sets the aws region that must be used. - // This is particularly relevant for API calls that must target a specific region's endpoint. - // Eg calling S3 APIs for buckets that are in another region. - SetAWSRegion(string) - - // RegionForCreateBucket is the AWS Region that should be used to create buckets. - RegionForCreateBucket() string - - // HTTPHead performs an HTTP request for the URL using the HEAD verb. - HTTPHead(ctx context.Context, url string) (resp *http.Response, err error) - // GetCallerIdentity returns information about the caller identity. GetCallerIdentity(ctx context.Context, params *sts.GetCallerIdentityInput, optFns ...func(*sts.Options)) (*sts.GetCallerIdentityOutput, error) @@ -188,18 +125,6 @@ type IdPIAMConfigureClient interface { // UpdateAssumeRolePolicy updates the policy that grants an IAM entity permission to assume a role. // This is typically referred to as the "role trust policy". UpdateAssumeRolePolicy(ctx context.Context, params *iam.UpdateAssumeRolePolicyInput, optFns ...func(*iam.Options)) (*iam.UpdateAssumeRolePolicyOutput, error) - - // CreateBucket creates an Amazon S3 bucket. - CreateBucket(ctx context.Context, params *s3.CreateBucketInput, optFns ...func(*s3.Options)) (*s3.CreateBucketOutput, error) - - // PutObject adds an object to a bucket. - PutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error) - - // HeadBucket checks if a bucket exists and if you have permission to access it. - HeadBucket(ctx context.Context, params *s3.HeadBucketInput, optFns ...func(*s3.Options)) (*s3.HeadBucketOutput, error) - - // DeletePublicAccessBlock removes the PublicAccessBlock configuration for an Amazon S3 bucket. - DeletePublicAccessBlock(ctx context.Context, params *s3.DeletePublicAccessBlockInput, optFns ...func(*s3.Options)) (*s3.DeletePublicAccessBlockOutput, error) } type defaultIdPIAMConfigureClient struct { @@ -208,7 +133,6 @@ type defaultIdPIAMConfigureClient struct { *iam.Client awsConfig aws.Config stsClient *sts.Client - s3Client *s3.Client } // GetCallerIdentity returns details about the IAM user or role whose credentials are used to call the operation. @@ -216,63 +140,6 @@ func (d *defaultIdPIAMConfigureClient) GetCallerIdentity(ctx context.Context, pa return d.stsClient.GetCallerIdentity(ctx, params, optFns...) } -// CreateBucket creates an Amazon S3 bucket. -func (d *defaultIdPIAMConfigureClient) CreateBucket(ctx context.Context, params *s3.CreateBucketInput, optFns ...func(*s3.Options)) (*s3.CreateBucketOutput, error) { - return d.s3Client.CreateBucket(ctx, params, optFns...) -} - -// PutObject adds an object to a bucket. -func (d *defaultIdPIAMConfigureClient) PutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error) { - return d.s3Client.PutObject(ctx, params, optFns...) -} - -// HeadBucket adds an object to a bucket. -func (d *defaultIdPIAMConfigureClient) HeadBucket(ctx context.Context, params *s3.HeadBucketInput, optFns ...func(*s3.Options)) (*s3.HeadBucketOutput, error) { - return d.s3Client.HeadBucket(ctx, params, optFns...) -} - -// PutBucketPolicy applies an Amazon S3 bucket policy to an Amazon S3 bucket. -func (d *defaultIdPIAMConfigureClient) PutBucketPolicy(ctx context.Context, params *s3.PutBucketPolicyInput, optFns ...func(*s3.Options)) (*s3.PutBucketPolicyOutput, error) { - return d.s3Client.PutBucketPolicy(ctx, params, optFns...) -} - -// DeletePublicAccessBlock removes the PublicAccessBlock configuration for an Amazon S3 bucket. -func (d *defaultIdPIAMConfigureClient) DeletePublicAccessBlock(ctx context.Context, params *s3.DeletePublicAccessBlockInput, optFns ...func(*s3.Options)) (*s3.DeletePublicAccessBlockOutput, error) { - return d.s3Client.DeletePublicAccessBlock(ctx, params, optFns...) -} - -// GetBucketPolicy returns the policy of a specified bucket -func (d *defaultIdPIAMConfigureClient) GetBucketPolicy(ctx context.Context, params *s3.GetBucketPolicyInput, optFns ...func(*s3.Options)) (*s3.GetBucketPolicyOutput, error) { - return d.s3Client.GetBucketPolicy(ctx, params, optFns...) -} - -// RegionForCreateBucket returns the region where the bucket should be created. -func (d *defaultIdPIAMConfigureClient) RegionForCreateBucket() string { - return d.awsConfig.Region -} - -// SetAWSRegion sets the aws region for next api calls. -func (d *defaultIdPIAMConfigureClient) SetAWSRegion(awsRegion string) { - if d.awsConfig.Region == awsRegion { - return - } - - d.awsConfig.Region = awsRegion - - // S3 Client is the only client that depends on the region. - d.s3Client = s3.NewFromConfig(d.awsConfig) -} - -// HTTPHead performs an HTTP request for the URL using the HEAD verb. -func (d *defaultIdPIAMConfigureClient) HTTPHead(ctx context.Context, url string) (*http.Response, error) { - req, err := http.NewRequest(http.MethodHead, url, nil) - if err != nil { - return nil, trace.Wrap(err) - } - - return d.httpClient.Do(req) -} - // NewIdPIAMConfigureClient creates a new IdPIAMConfigureClient. // The client is not thread safe. func NewIdPIAMConfigureClient(ctx context.Context) (IdPIAMConfigureClient, error) { @@ -295,13 +162,12 @@ func NewIdPIAMConfigureClient(ctx context.Context) (IdPIAMConfigureClient, error awsConfig: cfg, Client: iam.NewFromConfig(cfg), stsClient: sts.NewFromConfig(cfg), - s3Client: s3.NewFromConfig(cfg), }, nil } // ConfigureIdPIAM creates a new IAM OIDC IdP in AWS. // -// The Provider URL is Teleport's Public Address or the S3 bucket. +// The provider URL is Teleport's public address. // It also creates a new Role configured to trust the recently created IdP. // If the role already exists, it will create another trust relationship for the IdP (if it doesn't exist). // @@ -310,12 +176,6 @@ func NewIdPIAMConfigureClient(ctx context.Context) (IdPIAMConfigureClient, error // - iam:CreateRole // - iam:GetRole // - iam:UpdateAssumeRolePolicy -// -// If it's using the S3 bucket flow, the following are required as well: -// - s3:CreateBucket -// - s3:PutBucketPublicAccessBlock (used for s3:DeletePublicAccessBlock) -// - s3:ListBuckets (used for s3:HeadBucket) -// - s3:PutObject func ConfigureIdPIAM(ctx context.Context, clt IdPIAMConfigureClient, req IdPIAMConfigureRequest) error { if err := req.CheckAndSetDefaults(); err != nil { return trace.Wrap(err) @@ -339,51 +199,13 @@ func ConfigureIdPIAM(ctx context.Context, clt IdPIAMConfigureClient, req IdPIAMC return trace.Wrap(err) } - // Configuration stops here if there's no S3 bucket. - // It will use the teleport's public address as IdP issuer. - if req.s3Bucket == "" { - return nil - } - log := slog.With( - "bucket", req.s3Bucket, - "bucket_prefix", req.s3BucketPrefix, - ) - - log.InfoContext(ctx, "Creating bucket in region", "region", clt.RegionForCreateBucket()) - if err := ensureBucketIdPIAM(ctx, clt, req, log); err != nil { - return trace.Wrap(err) - } - - log.InfoContext(ctx, `Removing "Block all public access".`) - _, err := clt.DeletePublicAccessBlock(ctx, &s3.DeletePublicAccessBlockInput{ - Bucket: &req.s3Bucket, - ExpectedBucketOwner: &req.AccountID, - }) - if err != nil { - return trace.Wrap(err) - } - - log.InfoContext(ctx, "Uploading 'openid-configuration' and 'jwks' files.") - if err := uploadOpenIDPublicFiles(ctx, clt, req); err != nil { - return trace.Wrap(err) - } - return nil } func ensureOIDCIdPIAM(ctx context.Context, clt IdPIAMConfigureClient, req IdPIAMConfigureRequest) error { - var err error - // For S3 bucket setups the thumbprint is ignored, but the API still requires a parseable one. - // https://github.com/aws-actions/configure-aws-credentials/issues/357#issuecomment-1626357333 - // We pass this dummy one for those scenarios. - thumbprint := "afafafafafafafafafafafafafafafafafafafaf" - - // For set ups that use the ProxyPublicAddress, we still calculate the thumbprint. - if req.ProxyPublicAddress != "" { - thumbprint, err = ThumbprintIdP(ctx, req.ProxyPublicAddress) - if err != nil { - return trace.Wrap(err) - } + thumbprint, err := ThumbprintIdP(ctx, req.ProxyPublicAddress) + if err != nil { + return trace.Wrap(err) } _, err = clt.CreateOpenIDConnectProvider(ctx, &iam.CreateOpenIDConnectProviderInput{ @@ -404,79 +226,6 @@ func ensureOIDCIdPIAM(ctx context.Context, clt IdPIAMConfigureClient, req IdPIAM return nil } -func ensureBucketIdPIAM(ctx context.Context, clt IdPIAMConfigureClient, req IdPIAMConfigureRequest, log *slog.Logger) error { - // According to https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLocation.html - // s3:GetBucketLocation is not recommended, and should be replaced by s3:HeadBucket according to AWS docs. - // The issue with using s3:HeadBucket is that it returns an error if the SDK client's region is not the same as the bucket. - // Doing a HEAD HTTP request seems to be the best option - resp, err := clt.HTTPHead(ctx, fmt.Sprintf("https://s3.amazonaws.com/%s", req.s3Bucket)) - if err != nil { - return trace.Wrap(err) - } - defer resp.Body.Close() - - // Even if the bucket is private, the "x-amz-bucket-region" Header will be there. - bucketRegion := resp.Header.Get("x-amz-bucket-region") - if bucketRegion != "" { - if bucketRegion == "EU" { - bucketRegion = "eu-west-1" - } - - clt.SetAWSRegion(bucketRegion) - } - - headBucketResp, err := clt.HeadBucket(ctx, &s3.HeadBucketInput{ - Bucket: &req.s3Bucket, - ExpectedBucketOwner: &req.AccountID, - }) - if err == nil { - log.InfoContext(ctx, "Bucket already exists in region", "region", aws.ToString(headBucketResp.BucketRegion)) - return nil - } - awsErr := awslib.ConvertIAMv2Error(err) - if trace.IsNotFound(awsErr) { - _, err := clt.CreateBucket(ctx, &s3.CreateBucketInput{ - Bucket: &req.s3Bucket, - CreateBucketConfiguration: awsutil.CreateBucketConfiguration(clt.RegionForCreateBucket()), - ObjectOwnership: s3types.ObjectOwnershipBucketOwnerPreferred, - }) - return trace.Wrap(err) - } - - return trace.Wrap(awsErr) -} - -func uploadOpenIDPublicFiles(ctx context.Context, clt IdPIAMConfigureClient, req IdPIAMConfigureRequest) error { - openidConfigPath := path.Join(req.s3BucketPrefix, ".well-known/openid-configuration") - jwksBucketPath := path.Join(req.s3BucketPrefix, ".well-known/jwks") - jwksPublicURI, err := url.JoinPath(req.issuerURL, ".well-known/jwks") - if err != nil { - return trace.Wrap(err) - } - - openIDConfigJSON, err := json.Marshal(oidc.OpenIDConfigurationForIssuer(req.issuer, jwksPublicURI)) - if err != nil { - return trace.Wrap(err) - } - _, err = clt.PutObject(ctx, &s3.PutObjectInput{ - Bucket: &req.s3Bucket, - Key: &openidConfigPath, - Body: bytes.NewReader(openIDConfigJSON), - ACL: s3types.ObjectCannedACLPublicRead, - }) - if err != nil { - return trace.Wrap(err) - } - - _, err = clt.PutObject(ctx, &s3.PutObjectInput{ - Bucket: &req.s3Bucket, - Key: &jwksBucketPath, - Body: bytes.NewReader(req.jwksFileContents), - ACL: s3types.ObjectCannedACLPublicRead, - }) - return trace.Wrap(err) -} - func createIdPIAMRole(ctx context.Context, clt IdPIAMConfigureClient, req IdPIAMConfigureRequest) error { integrationRoleAssumeRoleDocument, err := awslib.NewPolicyDocument( awslib.StatementForAWSOIDCRoleTrustRelationship(req.AccountID, req.issuer, []string{types.IntegrationAWSOIDCAudience}), diff --git a/lib/integrations/awsoidc/idp_iam_config_test.go b/lib/integrations/awsoidc/idp_iam_config_test.go index 9b5b10d9bd7a0..4ae10d979c73b 100644 --- a/lib/integrations/awsoidc/idp_iam_config_test.go +++ b/lib/integrations/awsoidc/idp_iam_config_test.go @@ -20,9 +20,7 @@ package awsoidc import ( "context" - "encoding/base64" "fmt" - "net/http" "net/http/httptest" "net/url" "slices" @@ -32,7 +30,6 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/iam" iamTypes "github.com/aws/aws-sdk-go-v2/service/iam/types" - "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/sts" "github.com/gravitational/trace" "github.com/stretchr/testify/require" @@ -42,19 +39,7 @@ import ( ) func TestIdPIAMConfigReqDefaults(t *testing.T) { - base64EncodedString := base64.StdEncoding.EncodeToString([]byte(`jwks`)) - - baseIdPIAMConfigReqWithS3Bucket := func() IdPIAMConfigureRequest { - return IdPIAMConfigureRequest{ - Cluster: "mycluster", - IntegrationName: "myintegration", - IntegrationRole: "integrationrole", - S3BucketLocation: "s3://bucket-1/prefix-2", - S3JWKSContentsB64: base64EncodedString, - } - } - - baseIdPIAMConfigReqWithProxy := func() IdPIAMConfigureRequest { + baseIdPIAMConfigReq := func() IdPIAMConfigureRequest { return IdPIAMConfigureRequest{ Cluster: "mycluster", IntegrationName: "myintegration", @@ -70,8 +55,8 @@ func TestIdPIAMConfigReqDefaults(t *testing.T) { expected IdPIAMConfigureRequest }{ { - name: "proxy mode: set defaults", - req: baseIdPIAMConfigReqWithProxy, + name: "set defaults", + req: baseIdPIAMConfigReq, errCheck: require.NoError, expected: IdPIAMConfigureRequest{ Cluster: "mycluster", @@ -88,85 +73,18 @@ func TestIdPIAMConfigReqDefaults(t *testing.T) { }, }, { - name: "proxy mode: missing proxy public address", + name: "missing proxy public address", req: func() IdPIAMConfigureRequest { - req := baseIdPIAMConfigReqWithProxy() + req := baseIdPIAMConfigReq() req.ProxyPublicAddress = "" return req }, errCheck: badParameterCheck, }, - { - name: "s3 bucket mode: set defaults", - req: baseIdPIAMConfigReqWithS3Bucket, - errCheck: require.NoError, - expected: IdPIAMConfigureRequest{ - Cluster: "mycluster", - IntegrationName: "myintegration", - IntegrationRole: "integrationrole", - S3BucketLocation: "s3://bucket-1/prefix-2", - s3Bucket: "bucket-1", - s3BucketPrefix: "prefix-2", - jwksFileContents: []byte(`jwks`), - S3JWKSContentsB64: base64EncodedString, - issuer: "bucket-1.s3.amazonaws.com/prefix-2", - issuerURL: "https://bucket-1.s3.amazonaws.com/prefix-2", - ownershipTags: tags.AWSTags{ - "teleport.dev/cluster": "mycluster", - "teleport.dev/integration": "myintegration", - "teleport.dev/origin": "integration_awsoidc", - }, - }, - }, - { - name: "s3 bucket mode: missing jwks content", - req: func() IdPIAMConfigureRequest { - req := baseIdPIAMConfigReqWithS3Bucket() - req.S3JWKSContentsB64 = "" - return req - }, - errCheck: badParameterCheck, - }, - { - name: "s3 bucket mode: invalid jwks content", - req: func() IdPIAMConfigureRequest { - req := baseIdPIAMConfigReqWithS3Bucket() - req.S3JWKSContentsB64 = "x" - return req - }, - errCheck: badParameterCheck, - }, - { - name: "s3 bucket mode: invalid url for s3 location", - req: func() IdPIAMConfigureRequest { - req := baseIdPIAMConfigReqWithS3Bucket() - req.S3BucketLocation = "invalid-url" - return req - }, - errCheck: badParameterCheck, - }, - { - name: "s3 bucket mode: invalid schema for s3 location", - req: func() IdPIAMConfigureRequest { - req := baseIdPIAMConfigReqWithS3Bucket() - req.S3BucketLocation = "https://proxy.example.com" - return req - }, - errCheck: badParameterCheck, - }, - { - name: "proxy and s3 bucket defined", - req: func() IdPIAMConfigureRequest { - req := baseIdPIAMConfigReqWithProxy() - req.S3BucketLocation = "s3://bucket/prefix" - return req - }, - errCheck: badParameterCheck, - }, { name: "missing cluster", req: func() IdPIAMConfigureRequest { - req := baseIdPIAMConfigReqWithProxy() + req := baseIdPIAMConfigReq() req.Cluster = "" return req }, @@ -175,7 +93,7 @@ func TestIdPIAMConfigReqDefaults(t *testing.T) { { name: "missing integration name", req: func() IdPIAMConfigureRequest { - req := baseIdPIAMConfigReqWithProxy() + req := baseIdPIAMConfigReq() req.IntegrationName = "" return req }, @@ -184,7 +102,7 @@ func TestIdPIAMConfigReqDefaults(t *testing.T) { { name: "missing integration role", req: func() IdPIAMConfigureRequest { - req := baseIdPIAMConfigReqWithProxy() + req := baseIdPIAMConfigReq() req.IntegrationRole = "" return req }, @@ -230,357 +148,148 @@ func assumeRoleStatementJSON(issuer string) string { }`, issuer, issuer) } -func TestConfigureIdPIAMUsingProxyURL(t *testing.T) { +func TestConfigureIdPIAM(t *testing.T) { ctx := context.Background() - t.Run("using proxy url", func(t *testing.T) { - tlsServer := httptest.NewTLSServer(nil) - tlsServerURL, err := url.Parse(tlsServer.URL) - require.NoError(t, err) + tlsServer := httptest.NewTLSServer(nil) + tlsServerURL, err := url.Parse(tlsServer.URL) + require.NoError(t, err) - tlsServerIssuer := tlsServerURL.Host - // TLS Server starts with self-signed certificates. + tlsServerIssuer := tlsServerURL.Host + // TLS Server starts with self-signed certificates. - lib.SetInsecureDevMode(true) - defer lib.SetInsecureDevMode(false) + lib.SetInsecureDevMode(true) + defer lib.SetInsecureDevMode(false) - baseIdPIAMConfigReqWithTLServer := func() IdPIAMConfigureRequest { - return IdPIAMConfigureRequest{ - Cluster: "mycluster", - IntegrationName: "myintegration", - IntegrationRole: "integrationrole", - ProxyPublicAddress: tlsServer.URL, - } + baseIdPIAMConfigReqWithTLServer := func() IdPIAMConfigureRequest { + return IdPIAMConfigureRequest{ + Cluster: "mycluster", + IntegrationName: "myintegration", + IntegrationRole: "integrationrole", + ProxyPublicAddress: tlsServer.URL, } + } - for _, tt := range []struct { - name string - mockAccountID string - mockExistingRoles map[string]mockRole - mockExistingIdPUrl []string - req func() IdPIAMConfigureRequest - errCheck require.ErrorAssertionFunc - externalStateCheck func(*testing.T, mockIdPIAMConfigClient) - }{ - { - name: "valid", - mockAccountID: "123456789012", - req: baseIdPIAMConfigReqWithTLServer, - mockExistingIdPUrl: []string{}, - mockExistingRoles: map[string]mockRole{}, - errCheck: require.NoError, - }, - { - name: "idp url already exists", - mockAccountID: "123456789012", - mockExistingIdPUrl: []string{tlsServer.URL}, - mockExistingRoles: map[string]mockRole{}, - req: baseIdPIAMConfigReqWithTLServer, - errCheck: require.NoError, - }, - { - name: "role exists, no ownership tags", - mockAccountID: "123456789012", - mockExistingIdPUrl: []string{}, - mockExistingRoles: map[string]mockRole{"integrationrole": {}}, - req: baseIdPIAMConfigReqWithTLServer, - errCheck: badParameterCheck, - }, - { - name: "role exists, ownership tags, no assume role", - mockAccountID: "123456789012", - mockExistingIdPUrl: []string{}, - mockExistingRoles: map[string]mockRole{"integrationrole": { - tags: []iamTypes.Tag{ - {Key: aws.String("teleport.dev/origin"), Value: aws.String("integration_awsoidc")}, - {Key: aws.String("teleport.dev/cluster"), Value: aws.String("mycluster")}, - {Key: aws.String("teleport.dev/integration"), Value: aws.String("myintegration")}, - }, - assumeRolePolicyDoc: aws.String(`{"Version":"2012-10-17", "Statements":[]}`), - }}, - req: baseIdPIAMConfigReqWithTLServer, - errCheck: require.NoError, - externalStateCheck: func(t *testing.T, mipc mockIdPIAMConfigClient) { - role := mipc.existingRoles["integrationrole"] - expectedAssumeRolePolicyDoc := policyDocWithStatementsJSON( - assumeRoleStatementJSON(tlsServerIssuer), - ) - require.JSONEq(t, *expectedAssumeRolePolicyDoc, aws.ToString(role.assumeRolePolicyDoc)) + for _, tt := range []struct { + name string + mockAccountID string + mockExistingRoles map[string]mockRole + mockExistingIdPUrl []string + req func() IdPIAMConfigureRequest + errCheck require.ErrorAssertionFunc + externalStateCheck func(*testing.T, mockIdPIAMConfigClient) + }{ + { + name: "valid", + mockAccountID: "123456789012", + req: baseIdPIAMConfigReqWithTLServer, + mockExistingIdPUrl: []string{}, + mockExistingRoles: map[string]mockRole{}, + errCheck: require.NoError, + }, + { + name: "idp url already exists", + mockAccountID: "123456789012", + mockExistingIdPUrl: []string{tlsServer.URL}, + mockExistingRoles: map[string]mockRole{}, + req: baseIdPIAMConfigReqWithTLServer, + errCheck: require.NoError, + }, + { + name: "role exists, no ownership tags", + mockAccountID: "123456789012", + mockExistingIdPUrl: []string{}, + mockExistingRoles: map[string]mockRole{"integrationrole": {}}, + req: baseIdPIAMConfigReqWithTLServer, + errCheck: badParameterCheck, + }, + { + name: "role exists, ownership tags, no assume role", + mockAccountID: "123456789012", + mockExistingIdPUrl: []string{}, + mockExistingRoles: map[string]mockRole{"integrationrole": { + tags: []iamTypes.Tag{ + {Key: aws.String("teleport.dev/origin"), Value: aws.String("integration_awsoidc")}, + {Key: aws.String("teleport.dev/cluster"), Value: aws.String("mycluster")}, + {Key: aws.String("teleport.dev/integration"), Value: aws.String("myintegration")}, }, + assumeRolePolicyDoc: aws.String(`{"Version":"2012-10-17", "Statements":[]}`), + }}, + req: baseIdPIAMConfigReqWithTLServer, + errCheck: require.NoError, + externalStateCheck: func(t *testing.T, mipc mockIdPIAMConfigClient) { + role := mipc.existingRoles["integrationrole"] + expectedAssumeRolePolicyDoc := policyDocWithStatementsJSON( + assumeRoleStatementJSON(tlsServerIssuer), + ) + require.JSONEq(t, *expectedAssumeRolePolicyDoc, aws.ToString(role.assumeRolePolicyDoc)) }, - { - name: "role exists, ownership tags, with existing assume role", - mockAccountID: "123456789012", - mockExistingIdPUrl: []string{}, - mockExistingRoles: map[string]mockRole{"integrationrole": { - tags: []iamTypes.Tag{ - {Key: aws.String("teleport.dev/origin"), Value: aws.String("integration_awsoidc")}, - {Key: aws.String("teleport.dev/cluster"), Value: aws.String("mycluster")}, - {Key: aws.String("teleport.dev/integration"), Value: aws.String("myintegration")}, - }, - assumeRolePolicyDoc: policyDocWithStatementsJSON( - assumeRoleStatementJSON("some-other-issuer"), - ), - }}, - req: baseIdPIAMConfigReqWithTLServer, - errCheck: require.NoError, - externalStateCheck: func(t *testing.T, mipc mockIdPIAMConfigClient) { - role := mipc.existingRoles["integrationrole"] - expectedAssumeRolePolicyDoc := policyDocWithStatementsJSON( - assumeRoleStatementJSON("some-other-issuer"), - assumeRoleStatementJSON(tlsServerIssuer), - ) - require.JSONEq(t, *expectedAssumeRolePolicyDoc, aws.ToString(role.assumeRolePolicyDoc)) + }, + { + name: "role exists, ownership tags, with existing assume role", + mockAccountID: "123456789012", + mockExistingIdPUrl: []string{}, + mockExistingRoles: map[string]mockRole{"integrationrole": { + tags: []iamTypes.Tag{ + {Key: aws.String("teleport.dev/origin"), Value: aws.String("integration_awsoidc")}, + {Key: aws.String("teleport.dev/cluster"), Value: aws.String("mycluster")}, + {Key: aws.String("teleport.dev/integration"), Value: aws.String("myintegration")}, }, + assumeRolePolicyDoc: policyDocWithStatementsJSON( + assumeRoleStatementJSON("some-other-issuer"), + ), + }}, + req: baseIdPIAMConfigReqWithTLServer, + errCheck: require.NoError, + externalStateCheck: func(t *testing.T, mipc mockIdPIAMConfigClient) { + role := mipc.existingRoles["integrationrole"] + expectedAssumeRolePolicyDoc := policyDocWithStatementsJSON( + assumeRoleStatementJSON("some-other-issuer"), + assumeRoleStatementJSON(tlsServerIssuer), + ) + require.JSONEq(t, *expectedAssumeRolePolicyDoc, aws.ToString(role.assumeRolePolicyDoc)) }, - { - name: "role exists, ownership tags, assume role already exists", - mockAccountID: "123456789012", - mockExistingIdPUrl: []string{}, - mockExistingRoles: map[string]mockRole{"integrationrole": { - tags: []iamTypes.Tag{ - {Key: aws.String("teleport.dev/origin"), Value: aws.String("integration_awsoidc")}, - {Key: aws.String("teleport.dev/cluster"), Value: aws.String("mycluster")}, - {Key: aws.String("teleport.dev/integration"), Value: aws.String("myintegration")}, - }, - assumeRolePolicyDoc: policyDocWithStatementsJSON( - assumeRoleStatementJSON(tlsServerIssuer), - ), - }}, - req: baseIdPIAMConfigReqWithTLServer, - errCheck: require.NoError, - externalStateCheck: func(t *testing.T, mipc mockIdPIAMConfigClient) { - role := mipc.existingRoles["integrationrole"] - expectedAssumeRolePolicyDoc := policyDocWithStatementsJSON( - assumeRoleStatementJSON(tlsServerIssuer), - ) - require.JSONEq(t, *expectedAssumeRolePolicyDoc, aws.ToString(role.assumeRolePolicyDoc)) + }, + { + name: "role exists, ownership tags, assume role already exists", + mockAccountID: "123456789012", + mockExistingIdPUrl: []string{}, + mockExistingRoles: map[string]mockRole{"integrationrole": { + tags: []iamTypes.Tag{ + {Key: aws.String("teleport.dev/origin"), Value: aws.String("integration_awsoidc")}, + {Key: aws.String("teleport.dev/cluster"), Value: aws.String("mycluster")}, + {Key: aws.String("teleport.dev/integration"), Value: aws.String("myintegration")}, }, + assumeRolePolicyDoc: policyDocWithStatementsJSON( + assumeRoleStatementJSON(tlsServerIssuer), + ), + }}, + req: baseIdPIAMConfigReqWithTLServer, + errCheck: require.NoError, + externalStateCheck: func(t *testing.T, mipc mockIdPIAMConfigClient) { + role := mipc.existingRoles["integrationrole"] + expectedAssumeRolePolicyDoc := policyDocWithStatementsJSON( + assumeRoleStatementJSON(tlsServerIssuer), + ) + require.JSONEq(t, *expectedAssumeRolePolicyDoc, aws.ToString(role.assumeRolePolicyDoc)) }, - } { - t.Run(tt.name, func(t *testing.T) { - clt := mockIdPIAMConfigClient{ - accountID: tt.mockAccountID, - existingRoles: tt.mockExistingRoles, - existingIDPUrl: tt.mockExistingIdPUrl, - } - - err := ConfigureIdPIAM(ctx, &clt, tt.req()) - tt.errCheck(t, err) - - if tt.externalStateCheck != nil { - tt.externalStateCheck(t, clt) - } - }) - } - }) - - t.Run("using s3 bucket", func(t *testing.T) { - base64EncodedString := base64.StdEncoding.EncodeToString([]byte(`jwks`)) - - baseIdPIAMConfigReqWithS3Bucket := func() IdPIAMConfigureRequest { - return IdPIAMConfigureRequest{ - Cluster: "mycluster", - IntegrationName: "myintegration", - IntegrationRole: "integrationrole", - S3BucketLocation: "s3://bucket-1/prefix-2", - S3JWKSContentsB64: base64EncodedString, + }, + } { + t.Run(tt.name, func(t *testing.T) { + clt := mockIdPIAMConfigClient{ + accountID: tt.mockAccountID, + existingRoles: tt.mockExistingRoles, + existingIDPUrl: tt.mockExistingIdPUrl, } - } - expectedIssuer := "bucket-1.s3.amazonaws.com/prefix-2" - expectedIssuerURL := "https://" + expectedIssuer - - for _, tt := range []struct { - name string - mockAccountID string - mockExistingIdPUrl []string - mockExistingRoles map[string]mockRole - mockClientRegion string - mockExistingBuckets map[string]mockBucket - req func() IdPIAMConfigureRequest - errCheck require.ErrorAssertionFunc - externalStateCheck func(*testing.T, mockIdPIAMConfigClient) - }{ - { - name: "valid without any existing resources", - mockAccountID: "123456789012", - req: baseIdPIAMConfigReqWithS3Bucket, - mockExistingIdPUrl: []string{}, - mockExistingRoles: map[string]mockRole{}, - mockExistingBuckets: map[string]mockBucket{}, - mockClientRegion: "my-region", - errCheck: require.NoError, - externalStateCheck: func(t *testing.T, mipc mockIdPIAMConfigClient) { - // Check IdP creation - require.Contains(t, mipc.existingIDPUrl, expectedIssuerURL) - - // Check Role creation - role := mipc.existingRoles["integrationrole"] - expectedAssumeRolePolicyDoc := policyDocWithStatementsJSON( - assumeRoleStatementJSON(expectedIssuer), - ) - require.JSONEq(t, *expectedAssumeRolePolicyDoc, aws.ToString(role.assumeRolePolicyDoc)) - - // Check Bucket creation - require.Contains(t, mipc.existingBuckets, "bucket-1") - bucket := mipc.existingBuckets["bucket-1"] - require.Equal(t, "my-region", bucket.region) - require.False(t, bucket.publicAccessIsBlocked) - require.Equal(t, "BucketOwnerPreferred", bucket.ownership) - - jwksKey := "bucket-1/prefix-2/.well-known/jwks" - require.Contains(t, mipc.existingObjects, jwksKey) - require.Equal(t, "public-read", mipc.existingObjects[jwksKey].acl) - - openidconfigKey := "bucket-1/prefix-2/.well-known/openid-configuration" - require.Contains(t, mipc.existingObjects, openidconfigKey) - require.Equal(t, "public-read", mipc.existingObjects[openidconfigKey].acl) - }, - }, - { - name: "valid with an existing IdP set up using Proxy URL", - mockAccountID: "123456789012", - req: baseIdPIAMConfigReqWithS3Bucket, - mockExistingIdPUrl: []string{"https://proxy.example.com"}, - mockExistingRoles: map[string]mockRole{ - "integrationrole": { - tags: []iamTypes.Tag{ - {Key: aws.String("teleport.dev/origin"), Value: aws.String("integration_awsoidc")}, - {Key: aws.String("teleport.dev/cluster"), Value: aws.String("mycluster")}, - {Key: aws.String("teleport.dev/integration"), Value: aws.String("myintegration")}, - }, - assumeRolePolicyDoc: policyDocWithStatementsJSON( - assumeRoleStatementJSON("proxy.example.com"), - ), - }, - }, - mockExistingBuckets: map[string]mockBucket{}, - mockClientRegion: "my-region", - errCheck: require.NoError, - externalStateCheck: func(t *testing.T, mipc mockIdPIAMConfigClient) { - // IdP should be created and the existing one must not be deleted. - require.Contains(t, mipc.existingIDPUrl, expectedIssuerURL) - require.Contains(t, mipc.existingIDPUrl, "https://proxy.example.com") - - // The role must include the new statement and must not delete the previous one - role := mipc.existingRoles["integrationrole"] - expectedAssumeRolePolicyDoc := policyDocWithStatementsJSON( - assumeRoleStatementJSON("proxy.example.com"), - assumeRoleStatementJSON(expectedIssuer), - ) - require.JSONEq(t, *expectedAssumeRolePolicyDoc, aws.ToString(role.assumeRolePolicyDoc)) - - // Check Bucket creation - require.Contains(t, mipc.existingBuckets, "bucket-1") - bucket := mipc.existingBuckets["bucket-1"] - require.Equal(t, "my-region", bucket.region) - require.False(t, bucket.publicAccessIsBlocked) - require.Equal(t, "BucketOwnerPreferred", bucket.ownership) - }, - }, - { - name: "bucket already exists but is on another region", - mockAccountID: "123456789012", - req: baseIdPIAMConfigReqWithS3Bucket, - mockExistingIdPUrl: []string{}, - mockExistingRoles: map[string]mockRole{}, - mockExistingBuckets: map[string]mockBucket{ - "bucket-1": { - region: "another-region", - publicAccessIsBlocked: true, - ownership: "BucketOwnerPreferred", - }, - }, - mockClientRegion: "my-region", - errCheck: require.NoError, - externalStateCheck: func(t *testing.T, mipc mockIdPIAMConfigClient) { - // Check IdP creation - require.Contains(t, mipc.existingIDPUrl, expectedIssuerURL) - - // Check Role creation - role := mipc.existingRoles["integrationrole"] - expectedAssumeRolePolicyDoc := policyDocWithStatementsJSON( - assumeRoleStatementJSON(expectedIssuer), - ) - require.JSONEq(t, *expectedAssumeRolePolicyDoc, aws.ToString(role.assumeRolePolicyDoc)) - - // Check Bucket creation - require.Contains(t, mipc.existingBuckets, "bucket-1") - bucket := mipc.existingBuckets["bucket-1"] - require.False(t, bucket.publicAccessIsBlocked) - require.Equal(t, "BucketOwnerPreferred", bucket.ownership) - - // The last configured region must be the existing bucket's region. - require.Equal(t, "another-region", mipc.clientRegion) - }, - }, - { - name: "everything already exists", - mockAccountID: "123456789012", - req: baseIdPIAMConfigReqWithS3Bucket, - mockExistingIdPUrl: []string{"https://bucket-1.s3.amazonaws.com/prefix-2"}, - mockExistingRoles: map[string]mockRole{ - "integrationrole": { - tags: []iamTypes.Tag{ - {Key: aws.String("teleport.dev/origin"), Value: aws.String("integration_awsoidc")}, - {Key: aws.String("teleport.dev/cluster"), Value: aws.String("mycluster")}, - {Key: aws.String("teleport.dev/integration"), Value: aws.String("myintegration")}, - }, - assumeRolePolicyDoc: policyDocWithStatementsJSON( - assumeRoleStatementJSON("bucket-1.s3.amazonaws.com/prefix-2"), - ), - }, - }, - mockExistingBuckets: map[string]mockBucket{ - "bucket-1": { - region: "my-region", - publicAccessIsBlocked: true, - }, - }, - mockClientRegion: "my-region", - errCheck: require.NoError, - externalStateCheck: func(t *testing.T, mipc mockIdPIAMConfigClient) { - // Check IdP exists - require.Contains(t, mipc.existingIDPUrl, expectedIssuerURL) - // Check Role exists - role := mipc.existingRoles["integrationrole"] - expectedAssumeRolePolicyDoc := policyDocWithStatementsJSON( - assumeRoleStatementJSON(expectedIssuer), - ) - require.JSONEq(t, *expectedAssumeRolePolicyDoc, aws.ToString(role.assumeRolePolicyDoc)) - - // Check Bucket exists - require.Contains(t, mipc.existingBuckets, "bucket-1") - bucket := mipc.existingBuckets["bucket-1"] - require.False(t, bucket.publicAccessIsBlocked) - }, - }, - } { - t.Run(tt.name, func(t *testing.T) { - clt := mockIdPIAMConfigClient{ - accountID: tt.mockAccountID, - existingRoles: tt.mockExistingRoles, - existingIDPUrl: tt.mockExistingIdPUrl, - existingBuckets: tt.mockExistingBuckets, - clientRegion: tt.mockClientRegion, - } - - err := ConfigureIdPIAM(ctx, &clt, tt.req()) - tt.errCheck(t, err) - - if tt.externalStateCheck != nil { - tt.externalStateCheck(t, clt) - } - }) - } - }) -} + err := ConfigureIdPIAM(ctx, &clt, tt.req()) + tt.errCheck(t, err) -type mockBucket struct { - region string - publicAccessIsBlocked bool - ownership string + if tt.externalStateCheck != nil { + tt.externalStateCheck(t, clt) + } + }) + } } type mockRole struct { @@ -588,16 +297,10 @@ type mockRole struct { tags []iamTypes.Tag } -type mockObject struct { - acl string -} type mockIdPIAMConfigClient struct { - clientRegion string - accountID string - existingIDPUrl []string - existingRoles map[string]mockRole - existingBuckets map[string]mockBucket - existingObjects map[string]mockObject + accountID string + existingIDPUrl []string + existingRoles map[string]mockRole } // GetCallerIdentity returns information about the caller identity. @@ -671,95 +374,6 @@ func (m *mockIdPIAMConfigClient) UpdateAssumeRolePolicy(ctx context.Context, par return &iam.UpdateAssumeRolePolicyOutput{}, nil } -// CreateBucket creates an Amazon S3 bucket. -func (m *mockIdPIAMConfigClient) CreateBucket(ctx context.Context, params *s3.CreateBucketInput, optFns ...func(*s3.Options)) (*s3.CreateBucketOutput, error) { - m.existingBuckets[*params.Bucket] = mockBucket{ - publicAccessIsBlocked: true, - region: m.clientRegion, - ownership: string(params.ObjectOwnership), - } - return nil, nil -} - -// PutObject adds an object to a bucket. -func (m *mockIdPIAMConfigClient) PutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error) { - if m.existingObjects == nil { - m.existingObjects = map[string]mockObject{} - } - - objectKey := fmt.Sprintf("%s/%s", *params.Bucket, *params.Key) - - m.existingObjects[objectKey] = mockObject{ - acl: string(params.ACL), - } - return nil, nil -} - -// HeadBucket adds an object to a bucket. -func (m *mockIdPIAMConfigClient) HeadBucket(ctx context.Context, params *s3.HeadBucketInput, optFns ...func(*s3.Options)) (*s3.HeadBucketOutput, error) { - bucket, found := m.existingBuckets[*params.Bucket] - if !found { - return nil, trace.NotFound("bucket does not exist") - } - - return &s3.HeadBucketOutput{ - BucketRegion: &bucket.region, - }, nil -} - -// RegionForCreateBucket returns the default aws region to use when creating a bucket. -func (m *mockIdPIAMConfigClient) RegionForCreateBucket() string { - return m.clientRegion -} - -// SetAWSRegion sets the default aws region to use. -func (m *mockIdPIAMConfigClient) SetAWSRegion(awsRegion string) { - m.clientRegion = awsRegion -} - -// DeletePublicAccessBlock removes the PublicAccessBlock configuration for an Amazon S3 bucket. -func (m *mockIdPIAMConfigClient) DeletePublicAccessBlock(ctx context.Context, params *s3.DeletePublicAccessBlockInput, optFns ...func(*s3.Options)) (*s3.DeletePublicAccessBlockOutput, error) { - bucket, found := m.existingBuckets[*params.Bucket] - if !found { - return nil, trace.NotFound("bucket does not exist") - } - - bucket.publicAccessIsBlocked = false - m.existingBuckets[*params.Bucket] = bucket - - return &s3.DeletePublicAccessBlockOutput{}, nil -} - -// HTTPHead does an HEAD HTTP Request to the target URL. -func (m *mockIdPIAMConfigClient) HTTPHead(ctx context.Context, endpoint string) (*http.Response, error) { - endpointURL, err := url.Parse(endpoint) - if err != nil { - return nil, trace.Wrap(err) - } - - // check if bucket exists - // expected URL is: https://s3.amazonaws.com// - endpointURLPath := strings.TrimLeft(endpointURL.Path, "/") - bucketName := strings.Split(endpointURLPath, "/")[0] - - bucket, found := m.existingBuckets[bucketName] - if !found { - return &http.Response{ - StatusCode: http.StatusNotFound, - Body: http.NoBody, - }, nil - } - - m.clientRegion = bucket.region - - return &http.Response{ - Header: http.Header{ - "x-amz-bucket-region": []string{bucket.region}, - }, - Body: http.NoBody, - }, nil -} - func TestNewIdPIAMConfigureClient(t *testing.T) { t.Run("no aws_region env var, returns an error", func(t *testing.T) { _, err := NewIdPIAMConfigureClient(context.Background()) diff --git a/lib/integrations/awsoidc/list_subnets.go b/lib/integrations/awsoidc/list_subnets.go index 6cbaf3c7a62cd..2a77cd102db52 100644 --- a/lib/integrations/awsoidc/list_subnets.go +++ b/lib/integrations/awsoidc/list_subnets.go @@ -126,15 +126,8 @@ func ListSubnets(ctx context.Context, clt ListSubnetsClient, req ListSubnetsRequ func convertAWSSubnets(subnets []ec2Types.Subnet) []Subnet { ret := make([]Subnet, 0, len(subnets)) for _, s := range subnets { - var name string - for _, tag := range s.Tags { - if aws.ToString(tag.Key) == "Name" { - name = aws.ToString(tag.Value) - break - } - } ret = append(ret, Subnet{ - Name: name, + Name: nameFromEC2Tags(s.Tags), ID: aws.ToString(s.SubnetId), AvailabilityZone: aws.ToString(s.AvailabilityZone), }) diff --git a/lib/integrations/awsoidc/list_vpcs.go b/lib/integrations/awsoidc/list_vpcs.go new file mode 100644 index 0000000000000..2a6b022413bc9 --- /dev/null +++ b/lib/integrations/awsoidc/list_vpcs.go @@ -0,0 +1,122 @@ +/* + * 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 awsoidc + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2Types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/gravitational/trace" +) + +// ListVPCsRequest contains the required fields to list AWS VPCs. +type ListVPCsRequest struct { + // NextToken is the token to be used to fetch the next page. + // If empty, the first page is fetched. + NextToken string +} + +// VPC is the Teleport representation of an AWS VPC. +type VPC struct { + // Name is the VPC name. + // This is just a friendly name and should not be used for further API calls. + // It can be empty if the VPC was not given a "Name" tag. + Name string `json:"name"` + + // ID is the VPC ID, for example "vpc-0ee975135dEXAMPLE". + // This is the value that should be used when doing further API calls. + ID string `json:"id"` +} + +// ListVPCsResponse contains a page of VPCs. +type ListVPCsResponse struct { + // VPCs contains the page of VPCs. + VPCs []VPC `json:"vpcs"` + + // NextToken is used for pagination. + // If non-empty, it can be used to request the next page. + NextToken string `json:"nextToken"` +} + +// ListVPCsClient describes the required methods to list AWS VPCs. +type ListVPCsClient interface { + // DescribeVpcs describes VPCs. + DescribeVpcs(ctx context.Context, params *ec2.DescribeVpcsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeVpcsOutput, error) +} + +type defaultListVPCsClient struct { + *ec2.Client +} + +// NewListVPCsClient creates a new ListVPCsClient using an AWSClientRequest. +func NewListVPCsClient(ctx context.Context, req *AWSClientRequest) (ListVPCsClient, error) { + ec2Client, err := newEC2Client(ctx, req) + if err != nil { + return nil, trace.Wrap(err) + } + + return &defaultListVPCsClient{ + Client: ec2Client, + }, nil +} + +// ListVPCs calls the following AWS API: +// https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html +// It returns a list of VPCs and an optional NextToken that can be used to fetch the next page. +func ListVPCs(ctx context.Context, clt ListVPCsClient, req ListVPCsRequest) (*ListVPCsResponse, error) { + describeVPCsInput := &ec2.DescribeVpcsInput{MaxResults: aws.Int32(100)} + if req.NextToken != "" { + describeVPCsInput.NextToken = &req.NextToken + } + + resp, err := clt.DescribeVpcs(ctx, describeVPCsInput) + if err != nil { + return nil, trace.Wrap(err) + } + + return &ListVPCsResponse{ + NextToken: aws.ToString(resp.NextToken), + VPCs: convertAWSVPCs(resp.Vpcs), + }, nil +} + +func convertAWSVPCs(vpcs []ec2Types.Vpc) []VPC { + ret := make([]VPC, 0, len(vpcs)) + for _, v := range vpcs { + ret = append(ret, VPC{ + Name: nameFromEC2Tags(v.Tags), + ID: aws.ToString(v.VpcId), + }) + } + return ret +} + +// nameFromEC2Tags is a helper to find the display name of an ec2 resource based +// on the "Name" tag, if it exists. +// Returns an empty string if there is no "Name" tag. +func nameFromEC2Tags(tags []ec2Types.Tag) string { + for _, tag := range tags { + if aws.ToString(tag.Key) == "Name" { + return aws.ToString(tag.Value) + } + } + return "" +} diff --git a/lib/integrations/awsoidc/list_vpcs_test.go b/lib/integrations/awsoidc/list_vpcs_test.go new file mode 100644 index 0000000000000..dd34de093f8a9 --- /dev/null +++ b/lib/integrations/awsoidc/list_vpcs_test.go @@ -0,0 +1,214 @@ +/* + * 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 awsoidc + +import ( + "context" + "fmt" + "strconv" + "testing" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2Types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/google/go-cmp/cmp" + "github.com/gravitational/trace" + "github.com/stretchr/testify/require" +) + +var _ ListVPCsClient = (*mockListVPCsClient)(nil) + +type mockListVPCsClient struct { + pageSize int + vpcs []ec2Types.Vpc +} + +// Returns information about AWS VPCs. +// This API supports pagination. +func (m mockListVPCsClient) DescribeVpcs(ctx context.Context, params *ec2.DescribeVpcsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeVpcsOutput, error) { + requestedPage := 1 + + totalVPCs := len(m.vpcs) + + if params.NextToken != nil { + currentMarker, err := strconv.Atoi(*params.NextToken) + if err != nil { + return nil, trace.Wrap(err) + } + requestedPage = currentMarker + } + + sliceStart := m.pageSize * (requestedPage - 1) + sliceEnd := m.pageSize * requestedPage + if sliceEnd > totalVPCs { + sliceEnd = totalVPCs + } + + ret := &ec2.DescribeVpcsOutput{ + Vpcs: m.vpcs[sliceStart:sliceEnd], + } + + if sliceEnd < totalVPCs { + nextToken := strconv.Itoa(requestedPage + 1) + ret.NextToken = &nextToken + } + + return ret, nil +} + +func TestListVPCs(t *testing.T) { + ctx := context.Background() + + noErrorFunc := func(err error) bool { + return err == nil + } + + const pageSize = 100 + t.Run("pagination", func(t *testing.T) { + totalVPCs := 203 + + VPCs := make([]ec2Types.Vpc, 0, totalVPCs) + for i := 0; i < totalVPCs; i++ { + VPCs = append(VPCs, ec2Types.Vpc{ + VpcId: aws.String(fmt.Sprintf("VPC-%d", i)), + Tags: makeNameTags(fmt.Sprintf("MyVPC-%d", i)), + }) + } + + mockListClient := &mockListVPCsClient{ + pageSize: pageSize, + vpcs: VPCs, + } + + // First page must return pageSize number of VPCs + resp, err := ListVPCs(ctx, mockListClient, ListVPCsRequest{ + NextToken: "", + }) + require.NoError(t, err) + require.NotEmpty(t, resp.NextToken) + require.Len(t, resp.VPCs, pageSize) + nextPageToken := resp.NextToken + require.Equal(t, "VPC-0", resp.VPCs[0].ID) + require.Equal(t, "MyVPC-0", resp.VPCs[0].Name) + + // Second page must return pageSize number of Endpoints + resp, err = ListVPCs(ctx, mockListClient, ListVPCsRequest{ + NextToken: nextPageToken, + }) + require.NoError(t, err) + require.NotEmpty(t, resp.NextToken) + require.Len(t, resp.VPCs, pageSize) + nextPageToken = resp.NextToken + require.Equal(t, "VPC-100", resp.VPCs[0].ID) + require.Equal(t, "MyVPC-100", resp.VPCs[0].Name) + + // Third page must return only the remaining Endpoints and an empty nextToken + resp, err = ListVPCs(ctx, mockListClient, ListVPCsRequest{ + NextToken: nextPageToken, + }) + require.NoError(t, err) + require.Empty(t, resp.NextToken) + require.Len(t, resp.VPCs, 3) + require.Equal(t, "VPC-200", resp.VPCs[0].ID) + require.Equal(t, "MyVPC-200", resp.VPCs[0].Name) + }) + + for _, tt := range []struct { + name string + req ListVPCsRequest + mockVPCs []ec2Types.Vpc + errCheck func(error) bool + respCheck func(*testing.T, *ListVPCsResponse) + }{ + { + name: "valid for listing VPCs", + req: ListVPCsRequest{ + NextToken: "", + }, + mockVPCs: []ec2Types.Vpc{{ + VpcId: aws.String("VPC-123"), + Tags: makeNameTags("MyVPC-123"), + }}, + respCheck: func(t *testing.T, ldr *ListVPCsResponse) { + require.Len(t, ldr.VPCs, 1) + require.Empty(t, ldr.NextToken, "there is only 1 page of VPCs") + + want := VPC{ + ID: "VPC-123", + Name: "MyVPC-123", + } + require.Empty(t, cmp.Diff(want, ldr.VPCs[0])) + }, + errCheck: noErrorFunc, + }, + } { + t.Run(tt.name, func(t *testing.T) { + mockListClient := &mockListVPCsClient{ + pageSize: pageSize, + vpcs: tt.mockVPCs, + } + resp, err := ListVPCs(ctx, mockListClient, tt.req) + require.True(t, tt.errCheck(err), "unexpected err: %v", err) + if tt.respCheck != nil { + tt.respCheck(t, resp) + } + }) + } +} + +func TestConvertVPC(t *testing.T) { + for _, tt := range []struct { + name string + input []ec2Types.Vpc + expected []VPC + }{ + { + name: "no name tag", + input: []ec2Types.Vpc{{ + VpcId: aws.String("VPC-abc"), + Tags: []ec2Types.Tag{ + {Key: aws.String("foo"), Value: aws.String("bar")}, + }, + }}, + expected: []VPC{{ + Name: "", + ID: "VPC-abc", + }}, + }, + { + name: "with name tag", + input: []ec2Types.Vpc{{ + VpcId: aws.String("VPC-abc"), + Tags: []ec2Types.Tag{ + {Key: aws.String("foo"), Value: aws.String("bar")}, + {Key: aws.String("Name"), Value: aws.String("llama")}, + {Key: aws.String("baz"), Value: aws.String("qux")}, + }, + }}, + expected: []VPC{{ + Name: "llama", + ID: "VPC-abc", + }}, + }, + } { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.expected, convertAWSVPCs(tt.input)) + }) + } +} diff --git a/lib/joinserver/joinserver.go b/lib/joinserver/joinserver.go index 1fee288d99fae..5e56bd39e9c84 100644 --- a/lib/joinserver/joinserver.go +++ b/lib/joinserver/joinserver.go @@ -62,7 +62,7 @@ type joinServiceClient interface { // server. On the Auth Server, this is passed to auth.ServerWithRoles and // through to auth.Server to be handled. type JoinServiceGRPCServer struct { - *proto.UnimplementedJoinServiceServer + proto.UnimplementedJoinServiceServer joinServiceClient joinServiceClient clock clockwork.Clock diff --git a/lib/kube/proxy/forwarder.go b/lib/kube/proxy/forwarder.go index 38e0cef84c736..0d6b547bfb7be 100644 --- a/lib/kube/proxy/forwarder.go +++ b/lib/kube/proxy/forwarder.go @@ -162,10 +162,18 @@ type ForwarderConfig struct { TracerProvider oteltrace.TracerProvider // Tracer is used to start spans. tracer oteltrace.Tracer - // ConnTLSConfig is the TLS client configuration to use when connecting to - // the upstream Teleport proxy or Kubernetes service when forwarding requests - // using the forward identity (i.e. proxy impersonating a user) method. - ConnTLSConfig *tls.Config + // GetConnTLSCertificate returns the TLS client certificate to use when + // connecting to the upstream Teleport proxy or Kubernetes service when + // forwarding requests using the forward identity (i.e. proxy impersonating + // a user) method. Paired with GetConnTLSRoots and ConnTLSCipherSuites to + // generate the correct [*tls.Config] on demand. + GetConnTLSCertificate utils.GetCertificateFunc + // GetConnTLSRoots returns the [*x509.CertPool] used to validate TLS + // connections to the upstream Teleport proxy or Kubernetes service. + GetConnTLSRoots utils.GetRootsFunc + // ConnTLSCipherSuites optionally contains a list of TLS ciphersuites to use + // when connecting to the upstream Teleport Proxy or Kubernetes service. + ConnTLSCipherSuites []uint16 // ClusterFeaturesGetter is a function that returns the Teleport cluster licensed features. // It is used to determine if the cluster is licensed for Kubernetes usage. ClusterFeatures ClusterFeaturesGetter @@ -247,12 +255,12 @@ func (f *ForwarderConfig) CheckAndSetDefaults() error { switch f.KubeServiceType { case KubeService: case ProxyService, LegacyProxyService: - if f.ConnTLSConfig == nil { - return trace.BadParameter("missing parameter TLSConfig") + if f.GetConnTLSCertificate == nil { + return trace.BadParameter("missing parameter GetConnTLSCertificate") + } + if f.GetConnTLSRoots == nil { + return trace.BadParameter("missing parameter GetConnTLSRoots") } - // Reset the ServerName to ensure that the proxy does not use the - // proxy's hostname as the SNI when connecting to the Kubernetes service. - f.ConnTLSConfig.ServerName = "" default: return trace.BadParameter("unknown value for KubeServiceType") } diff --git a/lib/kube/proxy/forwarder_test.go b/lib/kube/proxy/forwarder_test.go index 982fd6bb8bcfa..3b209d940450f 100644 --- a/lib/kube/proxy/forwarder_test.go +++ b/lib/kube/proxy/forwarder_test.go @@ -52,6 +52,7 @@ import ( "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/types" apievents "github.com/gravitational/teleport/api/types/events" + "github.com/gravitational/teleport/api/utils/tlsutils" "github.com/gravitational/teleport/entitlements" "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/auth/testauthority" @@ -1646,17 +1647,18 @@ func TestForwarderTLSConfigCAs(t *testing.T) { clusterName := "leaf" // Create a cert pool with the cert from fixtures.TLSCACertPEM + caCert, err := tlsutils.ParseCertificatePEM([]byte(fixtures.TLSCACertPEM)) + require.NoError(t, err) certPool := x509.NewCertPool() - certPool.AppendCertsFromPEM([]byte(fixtures.TLSCACertPEM)) + certPool.AddCert(caCert) - // create the tls config used by the forwarder - originalTLSConfig := &tls.Config{} // create the auth server mock client clock := clockwork.NewFakeClock() cl, err := newMockCSRClient(clock) require.NoError(t, err) cl.leafClusterName = clusterName + var getConnTLSRootsCalled bool f := &Forwarder{ cfg: ForwarderConfig{ Keygen: testauthority.New(), @@ -1665,24 +1667,38 @@ func TestForwarderTLSConfigCAs(t *testing.T) { tracer: otel.Tracer(teleport.ComponentKube), KubeServiceType: ProxyService, CachingAuthClient: cl, - ConnTLSConfig: originalTLSConfig, + + GetConnTLSCertificate: func() (*tls.Certificate, error) { + return nil, nil + }, + GetConnTLSRoots: func() (*x509.CertPool, error) { + getConnTLSRootsCalled = true + return x509.NewCertPool(), nil + }, }, log: logrus.NewEntry(logrus.New()), ctx: context.Background(), } + // generate tlsConfig for the leaf cluster tlsConfig, err := f.getTLSConfigForLeafCluster(clusterName) require.NoError(t, err) - // ensure that the tlsConfig is a clone of the originalTLSConfig - require.NotSame(t, originalTLSConfig, tlsConfig, "expected tlsConfig to be different from originalTLSConfig") - // ensure that the tlsConfig has the certPool as the RootCAs - require.True(t, tlsConfig.RootCAs.Equal(certPool), "expected root CAs to be equal to certPool") + _ = tlsConfig.VerifyConnection(tls.ConnectionState{ + ServerName: "nonempty", + PeerCertificates: []*x509.Certificate{ + caCert, + }, + }) + require.False(t, getConnTLSRootsCalled) // generate tlsConfig for the local cluster _, localTLSConfig, err := f.newLocalClusterTransport(clusterName) require.NoError(t, err) - // ensure that the localTLSConfig is a clone of the originalTLSConfig - require.NotSame(t, originalTLSConfig, localTLSConfig, "expected localTLSConfig pointer to be different from originalTLSConfig") - // ensure that the localTLSConfig doesn't have the certPool as the RootCAs - require.False(t, localTLSConfig.RootCAs.Equal(certPool), "root CAs should not include certPool") + _ = localTLSConfig.VerifyConnection(tls.ConnectionState{ + ServerName: "nonempty", + PeerCertificates: []*x509.Certificate{ + caCert, + }, + }) + require.True(t, getConnTLSRootsCalled) } diff --git a/lib/kube/proxy/server.go b/lib/kube/proxy/server.go index 4d1843aab8b9b..f44a478b85a68 100644 --- a/lib/kube/proxy/server.go +++ b/lib/kube/proxy/server.go @@ -112,16 +112,6 @@ func (c *TLSServerConfig) CheckAndSetDefaults() error { if c.TLS == nil { return trace.BadParameter("missing parameter TLS") } - c.TLS.ClientAuth = tls.RequireAndVerifyClientCert - if c.TLS.ClientCAs == nil { - return trace.BadParameter("missing parameter TLS.ClientCAs") - } - if c.TLS.RootCAs == nil { - return trace.BadParameter("missing parameter TLS.RootCAs") - } - if len(c.TLS.Certificates) == 0 { - return trace.BadParameter("missing parameter TLS.Certificates") - } if c.AccessPoint == nil { return trace.BadParameter("missing parameter AccessPoint") } diff --git a/lib/kube/proxy/sess.go b/lib/kube/proxy/sess.go index ccd1ee620b119..b05352096f92b 100644 --- a/lib/kube/proxy/sess.go +++ b/lib/kube/proxy/sess.go @@ -23,6 +23,7 @@ import ( "fmt" "io" "net/http" + "path" "reflect" "slices" "strings" @@ -1325,7 +1326,7 @@ func (s *session) trackSession(p *party, policySet []*types.SessionTrackerPolicy SessionID: s.id.String(), Kind: string(types.KubernetesSessionKind), State: types.SessionState_SessionStatePending, - Hostname: s.podName, + Hostname: path.Join(s.podNamespace, s.podName), ClusterName: s.ctx.teleportCluster.name, KubernetesCluster: s.ctx.kubeClusterName, HostUser: p.Ctx.User.GetName(), @@ -1362,7 +1363,7 @@ func (s *session) trackSession(p *party, policySet []*types.SessionTrackerPolicy case err != nil: return trace.Wrap(err) // the tracker was created successfully - case err == nil: + default: s.tracker = tracker } diff --git a/lib/kube/proxy/sess_test.go b/lib/kube/proxy/sess_test.go index a25848766666b..469fe2c4df27a 100644 --- a/lib/kube/proxy/sess_test.go +++ b/lib/kube/proxy/sess_test.go @@ -281,16 +281,18 @@ func Test_session_trackSession(t *testing.T) { assertErr: require.NoError, }, } - for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { sess := &session{ log: logrus.New().WithField(teleport.ComponentKey, "test"), id: uuid.New(), req: &http.Request{ - URL: &url.URL{}, + URL: &url.URL{ + RawQuery: "command=command&command=arg1&command=arg2", + }, }, podName: "podName", + podNamespace: "podNamespace", accessEvaluator: auth.NewSessionAccessEvaluator(tt.args.policies, types.KubernetesSessionKind, "username"), ctx: authContext{ Context: authz.Context{ @@ -319,6 +321,18 @@ func Test_session_trackSession(t *testing.T) { } err := sess.trackSession(p, tt.args.policies) tt.assertErr(t, err) + if err != nil { + return + } + tracker := tt.args.authClient.(*mockSessionTrackerService).tracker + require.Equal(t, "username", tracker.GetHostUser()) + require.Equal(t, "name", tracker.GetClusterName()) + require.Equal(t, "kubeClusterName", tracker.GetKubeCluster()) + require.Equal(t, sess.id.String(), tracker.GetSessionID()) + require.Equal(t, []string{"command", "arg1", "arg2"}, tracker.GetCommand()) + require.Equal(t, "podNamespace/podName", tracker.GetHostname()) + require.Equal(t, types.KubernetesSessionKind, tracker.GetSessionKind()) + }) } } @@ -326,9 +340,11 @@ func Test_session_trackSession(t *testing.T) { type mockSessionTrackerService struct { authclient.ClientI returnErr bool + tracker types.SessionTracker } -func (m *mockSessionTrackerService) CreateSessionTracker(ctx context.Context, tracker types.SessionTracker) (types.SessionTracker, error) { +func (m *mockSessionTrackerService) CreateSessionTracker(_ context.Context, tracker types.SessionTracker) (types.SessionTracker, error) { + m.tracker = tracker if m.returnErr { return nil, trace.ConnectionProblem(nil, "mock error") } diff --git a/lib/kube/proxy/transport.go b/lib/kube/proxy/transport.go index aa947ddd09599..985b5487744c4 100644 --- a/lib/kube/proxy/transport.go +++ b/lib/kube/proxy/transport.go @@ -37,9 +37,9 @@ import ( "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/auth" + "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/reversetunnelclient" - "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/utils" ) @@ -195,8 +195,8 @@ func (f *Forwarder) getTLSConfigForLeafCluster(clusterName string) (*tls.Config, ctx, cancel := context.WithTimeout(f.ctx, 5*time.Second) defer cancel() // Get the host CA for the target cluster from Auth to ensure we trust the - // leaf proxy certificate. - hostCA, err := f.cfg.CachingAuthClient.GetCertAuthority(ctx, types.CertAuthID{ + // leaf proxy certificate at the current time. + _, err := f.cfg.CachingAuthClient.GetCertAuthority(ctx, types.CertAuthID{ Type: types.HostCA, DomainName: clusterName, }, false) @@ -204,15 +204,22 @@ func (f *Forwarder) getTLSConfigForLeafCluster(clusterName string) (*tls.Config, return nil, trace.Wrap(err) } - pool := x509.NewCertPool() - for _, certAuthority := range services.GetTLSCerts(hostCA) { - if ok := pool.AppendCertsFromPEM(certAuthority); !ok { - return nil, trace.BadParameter("failed to append certificates, check that kubeconfig has correctly encoded certificate authority data") + tlsConfig := utils.TLSConfig(f.cfg.ConnTLSCipherSuites) + tlsConfig.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) { + tlsCert, err := f.cfg.GetConnTLSCertificate() + if err != nil { + return nil, trace.Wrap(err) } + return tlsCert, nil } - // Clone the TLS config and set the root CAs to the leaf host CA pool. - tlsConfig := f.cfg.ConnTLSConfig.Clone() - tlsConfig.RootCAs = pool + tlsConfig.InsecureSkipVerify = true + tlsConfig.VerifyConnection = utils.VerifyConnectionWithRoots(func() (*x509.CertPool, error) { + pool, _, err := authclient.ClientCertPool(f.ctx, f.cfg.CachingAuthClient, clusterName, types.HostCA) + if err != nil { + return nil, trace.Wrap(err) + } + return pool, nil + }) return tlsConfig, nil } @@ -258,9 +265,20 @@ func (f *Forwarder) remoteClusterDialer(clusterName string) dialContextFunc { // newLocalClusterTransport returns a new [http.Transport] (https://golang.org/pkg/net/http/#Transport) // that can be used to dial Kubernetes Service in a local Teleport cluster. func (f *Forwarder) newLocalClusterTransport(kubeClusterName string) (http.RoundTripper, *tls.Config, error) { + tlsConfig := utils.TLSConfig(f.cfg.ConnTLSCipherSuites) + tlsConfig.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) { + tlsCert, err := f.cfg.GetConnTLSCertificate() + if err != nil { + return nil, trace.Wrap(err) + } + return tlsCert, nil + } + tlsConfig.InsecureSkipVerify = true + tlsConfig.VerifyConnection = utils.VerifyConnectionWithRoots(f.cfg.GetConnTLSRoots) + dialFn := f.localClusterDialer(kubeClusterName) // Create a new HTTP/2 transport that will be used to dial the remote cluster. - h2Transport, err := newH2Transport(f.cfg.ConnTLSConfig, dialFn) + h2Transport, err := newH2Transport(tlsConfig, dialFn) if err != nil { return nil, nil, trace.Wrap(err) } @@ -268,7 +286,7 @@ func (f *Forwarder) newLocalClusterTransport(kubeClusterName string) (http.Round return instrumentedRoundtripper( f.cfg.KubeServiceType, auth.NewImpersonatorRoundTripper(h2Transport), - ), f.cfg.ConnTLSConfig.Clone(), nil + ), tlsConfig.Clone(), nil } // localClusterDialer returns a dialer that can be used to dial Kubernetes Service diff --git a/lib/kube/proxy/utils_testing.go b/lib/kube/proxy/utils_testing.go index 95997812c6c81..7297da9871414 100644 --- a/lib/kube/proxy/utils_testing.go +++ b/lib/kube/proxy/utils_testing.go @@ -20,6 +20,8 @@ package proxy import ( "context" + "crypto/tls" + "crypto/x509" "errors" "net" "net/http" @@ -313,6 +315,9 @@ func SetupTestContext(ctx context.Context, t *testing.T, cfg TestConfig) *TestCo require.NoError(t, err) proxyTLSConfig, err := proxyServerIdentity.TLSConfig(nil) require.NoError(t, err) + require.Len(t, proxyTLSConfig.Certificates, 1) + require.NotNil(t, proxyTLSConfig.RootCAs) + // Create kubernetes service server. testCtx.KubeProxy, err = NewTLSServer(TLSServerConfig{ ForwarderConfig: ForwarderConfig{ @@ -349,8 +354,13 @@ func SetupTestContext(ctx context.Context, t *testing.T, cfg TestConfig) *TestCo LockWatcher: testCtx.lockWatcher, Clock: clockwork.NewRealClock(), ClusterFeatures: features, - ConnTLSConfig: proxyTLSConfig.Clone(), - PROXYSigner: &multiplexer.PROXYSigner{}, + GetConnTLSCertificate: func() (*tls.Certificate, error) { + return &proxyTLSConfig.Certificates[0], nil + }, + GetConnTLSRoots: func() (*x509.CertPool, error) { + return proxyTLSConfig.RootCAs, nil + }, + PROXYSigner: &multiplexer.PROXYSigner{}, }, TLS: proxyTLSConfig.Clone(), AccessPoint: client, diff --git a/lib/kubernetestoken/token_source.go b/lib/kubernetestoken/token_source.go index 5d40296f9dd6b..55a506937cc89 100644 --- a/lib/kubernetestoken/token_source.go +++ b/lib/kubernetestoken/token_source.go @@ -24,7 +24,10 @@ import ( "github.com/gravitational/trace" ) -const kubernetesDefaultTokenPath = "/var/run/secrets/kubernetes.io/serviceaccount/token" +const ( + kubernetesDefaultTokenPath = "/var/run/secrets/kubernetes.io/serviceaccount/token" + EnvVarCustomKubernetesTokenPath = "KUBERNETES_TOKEN_PATH" +) type getEnvFunc func(key string) string type readFileFunc func(name string) ([]byte, error) @@ -33,7 +36,7 @@ func GetIDToken(getEnv getEnvFunc, readFile readFileFunc) (string, error) { // We check if we should use a custom location instead of the default one. This env var is not standard. // This is useful when the operator wants to use a custom projected token, or another service account. path := kubernetesDefaultTokenPath - if customPath := getEnv("KUBERNETES_TOKEN_PATH"); customPath != "" { + if customPath := getEnv(EnvVarCustomKubernetesTokenPath); customPath != "" { path = customPath } diff --git a/lib/multiplexer/test/ping_grpc.pb.go b/lib/multiplexer/test/ping_grpc.pb.go index 4c882f1a4aae8..33e683f0f58af 100644 --- a/lib/multiplexer/test/ping_grpc.pb.go +++ b/lib/multiplexer/test/ping_grpc.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.0 // - protoc (unknown) // source: teleport/lib/multiplexer/test/ping.proto @@ -32,8 +32,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( Pinger_Ping_FullMethodName = "/teleport.lib.multiplexer.test.Pinger/Ping" @@ -68,7 +68,7 @@ func (c *pingerClient) Ping(ctx context.Context, in *Request, opts ...grpc.CallO // PingerServer is the server API for Pinger service. // All implementations must embed UnimplementedPingerServer -// for forward compatibility +// for forward compatibility. // // Pinger is a service used in tests type PingerServer interface { @@ -76,14 +76,18 @@ type PingerServer interface { mustEmbedUnimplementedPingerServer() } -// UnimplementedPingerServer must be embedded to have forward compatible implementations. -type UnimplementedPingerServer struct { -} +// UnimplementedPingerServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedPingerServer struct{} func (UnimplementedPingerServer) Ping(context.Context, *Request) (*Response, error) { return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented") } func (UnimplementedPingerServer) mustEmbedUnimplementedPingerServer() {} +func (UnimplementedPingerServer) testEmbeddedByValue() {} // UnsafePingerServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to PingerServer will @@ -93,6 +97,13 @@ type UnsafePingerServer interface { } func RegisterPingerServer(s grpc.ServiceRegistrar, srv PingerServer) { + // If the following call pancis, it indicates UnimplementedPingerServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&Pinger_ServiceDesc, srv) } diff --git a/lib/proxy/peer/client.go b/lib/proxy/peer/client.go index 36d70b87779f6..6ce7958de0f25 100644 --- a/lib/proxy/peer/client.go +++ b/lib/proxy/peer/client.go @@ -38,16 +38,17 @@ import ( clientapi "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/metadata" "github.com/gravitational/teleport/api/types" + apiutils "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/api/utils/grpc/interceptors" streamutils "github.com/gravitational/teleport/api/utils/grpc/stream" "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/services" + "github.com/gravitational/teleport/lib/utils" ) // AccessPoint is the subset of the auth cache consumed by the [Client]. type AccessPoint interface { - authclient.CAGetter types.Events } @@ -61,8 +62,14 @@ type ClientConfig struct { AuthClient authclient.ClientI // AccessPoint is a caching auth client AccessPoint AccessPoint - // TLSConfig is the proxy client TLS configuration. - TLSConfig *tls.Config + // GetTLSCertificate returns a the client TLS certificate to use when + // connecting to other proxies. + GetTLSCertificate utils.GetCertificateFunc + // GetTLSRoots returns a certificate pool used to validate TLS connections + // to other proxies. + GetTLSRoots utils.GetRootsFunc + // TLSCipherSuites optionally contains a list of TLS ciphersuites to use. + TLSCipherSuites []uint16 // Log is the proxy client logger. Log logrus.FieldLogger // Clock is used to control connection monitoring ticker. @@ -73,10 +80,6 @@ type ClientConfig struct { // ClusterName is the name of the cluster. ClusterName string - // getConfigForServer updates the client tls config. - // configurable for testing purposes. - getConfigForServer func() (*tls.Config, error) - // connShuffler determines the order client connections will be used. connShuffler connShuffler @@ -141,22 +144,17 @@ func (c *ClientConfig) checkAndSetDefaults() error { return trace.BadParameter("missing cluster name") } - if c.TLSConfig == nil { - return trace.BadParameter("missing tls config") + if c.GetTLSCertificate == nil { + return trace.BadParameter("missing tls certificate getter") } - - if len(c.TLSConfig.Certificates) == 0 { - return trace.BadParameter("missing tls certificate") + if c.GetTLSRoots == nil { + return trace.BadParameter("missing tls roots getter") } if c.connShuffler == nil { c.connShuffler = randomConnShuffler() } - if c.getConfigForServer == nil { - c.getConfigForServer = getConfigForServer(c.Context, c.TLSConfig, c.AccessPoint, c.Log, c.ClusterName) - } - return nil } @@ -642,10 +640,17 @@ func (c *Client) getConnections(proxyIDs []string) ([]*clientConn, bool, error) // connect dials a new connection to proxyAddr. func (c *Client) connect(peerID string, peerAddr string) (*clientConn, error) { - tlsConfig, err := c.config.getConfigForServer() - if err != nil { - return nil, trace.Wrap(err, "Error updating client tls config") + tlsConfig := utils.TLSConfig(c.config.TLSCipherSuites) + tlsConfig.ServerName = apiutils.EncodeClusterName(c.config.ClusterName) + tlsConfig.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) { + tlsCert, err := c.config.GetTLSCertificate() + if err != nil { + return nil, trace.Wrap(err) + } + return tlsCert, nil } + tlsConfig.InsecureSkipVerify = true + tlsConfig.VerifyConnection = utils.VerifyConnectionWithRoots(c.config.GetTLSRoots) expectedPeer := authclient.HostFQDN(peerID, c.config.ClusterName) diff --git a/lib/proxy/peer/client_test.go b/lib/proxy/peer/client_test.go index 017eeb8b3f4dd..05f87c6610c3f 100644 --- a/lib/proxy/peer/client_test.go +++ b/lib/proxy/peer/client_test.go @@ -20,8 +20,6 @@ package peer import ( "context" - "crypto/tls" - "crypto/x509" "testing" "time" @@ -38,7 +36,7 @@ import ( func TestClientConn(t *testing.T) { ca := newSelfSignedCA(t) - client := setupClient(t, ca, ca, types.RoleProxy) + client := setupClient(t, ca, newAtomicCA(ca), types.RoleProxy) _, def1 := setupServer(t, "s1", ca, ca, types.RoleProxy) server2, def2 := setupServer(t, "s2", ca, ca, types.RoleProxy) @@ -81,7 +79,7 @@ func TestClientConn(t *testing.T) { func TestClientUpdate(t *testing.T) { ca := newSelfSignedCA(t) - client := setupClient(t, ca, ca, types.RoleProxy) + client := setupClient(t, ca, newAtomicCA(ca), types.RoleProxy) _, def1 := setupServer(t, "s1", ca, ca, types.RoleProxy) server2, def2 := setupServer(t, "s2", ca, ca, types.RoleProxy) @@ -137,8 +135,9 @@ func TestClientUpdate(t *testing.T) { func TestCAChange(t *testing.T) { clientCA := newSelfSignedCA(t) serverCA := newSelfSignedCA(t) + currentServerCA := newAtomicCA(serverCA) - client := setupClient(t, clientCA, serverCA, types.RoleProxy) + client := setupClient(t, clientCA, currentServerCA, types.RoleProxy) server, _ := setupServer(t, "s1", serverCA, clientCA, types.RoleProxy) // dial server and send a test data frame @@ -167,13 +166,7 @@ func TestCAChange(t *testing.T) { // new connection should succeed because client tls config references new // RootCAs. - client.config.getConfigForServer = func() (*tls.Config, error) { - config := client.config.TLSConfig.Clone() - rootCAs := x509.NewCertPool() - rootCAs.AddCert(newServerCA.Cert) - config.RootCAs = rootCAs - return config, nil - } + currentServerCA.Store(newServerCA) conn, err = client.connect("s1", server.config.Listener.Addr().String()) require.NoError(t, err) @@ -185,7 +178,7 @@ func TestCAChange(t *testing.T) { func TestBackupClient(t *testing.T) { ca := newSelfSignedCA(t) - client := setupClient(t, ca, ca, types.RoleProxy) + client := setupClient(t, ca, newAtomicCA(ca), types.RoleProxy) dialCalled := false // Force the first client connection to fail. diff --git a/lib/proxy/peer/credentials.go b/lib/proxy/peer/credentials.go index 3c7c9026a0320..e6dfc29160cb4 100644 --- a/lib/proxy/peer/credentials.go +++ b/lib/proxy/peer/credentials.go @@ -20,8 +20,6 @@ package peer import ( "context" - "crypto/tls" - "crypto/x509" "net" "github.com/gravitational/trace" @@ -29,7 +27,6 @@ import ( "google.golang.org/grpc/credentials" "github.com/gravitational/teleport/api/types" - "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/tlsca" ) @@ -164,48 +161,3 @@ func validatePeer(peerID string, identity *tlsca.Identity) error { return trace.AccessDenied("connected to unexpected proxy") } - -// getConfigForClient clones and updates the server's tls config with the -// appropriate client certificate authorities. -func getConfigForClient(tlsConfig *tls.Config, ap authclient.CAGetter, log logrus.FieldLogger, clusterName string) func(*tls.ClientHelloInfo) (*tls.Config, error) { - return func(info *tls.ClientHelloInfo) (*tls.Config, error) { - tlsCopy := tlsConfig.Clone() - - pool, err := getCertPool(info.Context(), ap, clusterName) - if err != nil { - log.WithError(err).Error("Failed to retrieve client CA pool.") - return tlsCopy, nil - } - - tlsCopy.ClientAuth = tls.RequireAndVerifyClientCert - tlsCopy.ClientCAs = pool - return tlsCopy, nil - } -} - -// getConfigForServer clones and updates the client's tls config with the -// appropriate server certificate authorities. -func getConfigForServer(ctx context.Context, tlsConfig *tls.Config, ap authclient.CAGetter, log logrus.FieldLogger, clusterName string) func() (*tls.Config, error) { - return func() (*tls.Config, error) { - tlsCopy := tlsConfig.Clone() - - pool, err := getCertPool(ctx, ap, clusterName) - if err != nil { - log.WithError(err).Error("Failed to retrieve server CA pool.") - return tlsCopy, nil - } - - tlsCopy.RootCAs = pool - return tlsCopy, nil - } -} - -// getCertPool returns a new cert pool from cache if any. -func getCertPool(ctx context.Context, ap authclient.CAGetter, clusterName string) (*x509.CertPool, error) { - pool, _, err := authclient.ClientCertPool(ctx, ap, clusterName, types.HostCA) - if err != nil { - return nil, trace.Wrap(err) - } - - return pool, nil -} diff --git a/lib/proxy/peer/helpers_test.go b/lib/proxy/peer/helpers_test.go index a07a4fd01f1dc..a5217966ee96d 100644 --- a/lib/proxy/peer/helpers_test.go +++ b/lib/proxy/peer/helpers_test.go @@ -25,6 +25,7 @@ import ( "crypto/x509/pkix" "encoding/pem" "net" + "sync/atomic" "testing" "time" @@ -35,6 +36,7 @@ import ( clientapi "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/types" + apiutils "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/auth/native" "github.com/gravitational/teleport/lib/defaults" @@ -50,10 +52,6 @@ func (c mockAuthClient) GetProxies() ([]types.Server, error) { return []types.Server{}, nil } -type mockCAGetter struct { - authclient.CAGetter -} - type mockProxyAccessPoint struct { AccessPoint } @@ -143,8 +141,14 @@ func newSelfSignedCA(t *testing.T) *tlsca.CertAuthority { return ca } +func newAtomicCA(ca *tlsca.CertAuthority) *atomic.Pointer[tlsca.CertAuthority] { + a := new(atomic.Pointer[tlsca.CertAuthority]) + a.Store(ca) + return a +} + // certFromIdentity creates a tls config for a given CA and identity. -func certFromIdentity(t *testing.T, ca *tlsca.CertAuthority, ident tlsca.Identity) *tls.Config { +func certFromIdentity(t *testing.T, ca *tlsca.CertAuthority, ident tlsca.Identity) tls.Certificate { if ident.Username == "" { ident.Username = "test-user" } @@ -162,7 +166,7 @@ func certFromIdentity(t *testing.T, ca *tlsca.CertAuthority, ident tlsca.Identit PublicKey: privateKey.Public(), Subject: subj, NotAfter: clock.Now().UTC().Add(time.Minute), - DNSNames: []string{"127.0.0.1"}, + DNSNames: []string{"127.0.0.1", apiutils.EncodeClusterName("test")}, } certBytes, err := ca.GenerateCertificate(request) require.NoError(t, err) @@ -171,35 +175,31 @@ func certFromIdentity(t *testing.T, ca *tlsca.CertAuthority, ident tlsca.Identit cert, err := tls.X509KeyPair(certBytes, keyPEM) require.NoError(t, err) - config := &tls.Config{ - Certificates: []tls.Certificate{cert}, - } - - return config + return cert } // setupClients return a Client object. -func setupClient(t *testing.T, clientCA, serverCA *tlsca.CertAuthority, role types.SystemRole) *Client { - tlsConf := certFromIdentity(t, clientCA, tlsca.Identity{ +func setupClient(t *testing.T, clientCA *tlsca.CertAuthority, serverCA *atomic.Pointer[tlsca.CertAuthority], role types.SystemRole) *Client { + tlsCert := certFromIdentity(t, clientCA, tlsca.Identity{ Groups: []string{string(role)}, }) - getConfigForServer := func() (*tls.Config, error) { - config := tlsConf.Clone() - rootCAs := x509.NewCertPool() - rootCAs.AddCert(serverCA.Cert) - config.RootCAs = rootCAs - return config, nil - } - client, err := NewClient(ClientConfig{ - ID: "client-proxy", - AuthClient: mockAuthClient{}, - AccessPoint: &mockProxyAccessPoint{}, - TLSConfig: tlsConf, + ID: "client-proxy", + AuthClient: mockAuthClient{}, + AccessPoint: &mockProxyAccessPoint{}, + + GetTLSCertificate: func() (*tls.Certificate, error) { + return &tlsCert, nil + }, + GetTLSRoots: func() (*x509.CertPool, error) { + pool := x509.NewCertPool() + ca := serverCA.Load() + pool.AddCert(ca.Cert) + return pool, nil + }, Clock: clockwork.NewFakeClock(), GracefulShutdownTimeout: time.Second, - getConfigForServer: getConfigForServer, sync: func() {}, connShuffler: noopConnShuffler(), ClusterName: "test", @@ -215,31 +215,25 @@ type serverTestOption func(*ServerConfig) // setupServer return a Server object. func setupServer(t *testing.T, name string, serverCA, clientCA *tlsca.CertAuthority, role types.SystemRole, options ...serverTestOption) (*Server, types.Server) { - tlsConf := certFromIdentity(t, serverCA, tlsca.Identity{ + tlsCert := certFromIdentity(t, serverCA, tlsca.Identity{ Username: name + ".test", Groups: []string{string(role)}, }) - - getConfigForClient := func(chi *tls.ClientHelloInfo) (*tls.Config, error) { - config := tlsConf.Clone() - config.ClientAuth = tls.RequireAndVerifyClientCert - clientCAs := x509.NewCertPool() - clientCAs.AddCert(clientCA.Cert) - config.ClientCAs = clientCAs - return config, nil + tlsConf := &tls.Config{ + Certificates: []tls.Certificate{tlsCert}, } + tlsConf.ClientCAs = x509.NewCertPool() + tlsConf.ClientCAs.AddCert(clientCA.Cert) listener, err := net.Listen("tcp", "localhost:0") require.NoError(t, err) config := ServerConfig{ - AccessCache: &mockCAGetter{}, - Listener: listener, - TLSConfig: tlsConf, - ClusterDialer: &mockClusterDialer{}, - getConfigForClient: getConfigForClient, - service: &mockProxyService{}, - ClusterName: "test", + Listener: listener, + TLSConfig: tlsConf, + ClusterDialer: &mockClusterDialer{}, + service: &mockProxyService{}, + ClusterName: "test", } for _, option := range options { option(&config) diff --git a/lib/proxy/peer/server.go b/lib/proxy/peer/server.go index ec46bb8697588..bb9015954ebef 100644 --- a/lib/proxy/peer/server.go +++ b/lib/proxy/peer/server.go @@ -35,7 +35,6 @@ import ( "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/metadata" "github.com/gravitational/teleport/api/utils/grpc/interceptors" - "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/utils" ) @@ -46,17 +45,12 @@ const ( // ServerConfig configures a Server instance. type ServerConfig struct { - AccessCache authclient.CAGetter Listener net.Listener TLSConfig *tls.Config ClusterDialer ClusterDialer Log logrus.FieldLogger ClusterName string - // getConfigForClient gets the client tls config. - // configurable for testing purposes. - getConfigForClient func(*tls.ClientHelloInfo) (*tls.Config, error) - // service is a custom ProxyServiceServer // configurable for testing purposes. service proto.ProxyServiceServer @@ -72,10 +66,6 @@ func (c *ServerConfig) checkAndSetDefaults() error { teleport.Component(teleport.ComponentProxy, "peer"), ) - if c.AccessCache == nil { - return trace.BadParameter("missing access cache") - } - if c.Listener == nil { return trace.BadParameter("missing listener") } @@ -91,19 +81,8 @@ func (c *ServerConfig) checkAndSetDefaults() error { if c.TLSConfig == nil { return trace.BadParameter("missing tls config") } - - if len(c.TLSConfig.Certificates) == 0 { - return trace.BadParameter("missing tls certificate") - } - - c.TLSConfig = c.TLSConfig.Clone() c.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert - if c.getConfigForClient == nil { - c.getConfigForClient = getConfigForClient(c.TLSConfig, c.AccessCache, c.Log, c.ClusterName) - } - c.TLSConfig.GetConfigForClient = c.getConfigForClient - if c.service == nil { c.service = &proxyService{ c.ClusterDialer, diff --git a/lib/proxy/peer/server_test.go b/lib/proxy/peer/server_test.go index 0f88b332c4864..3ff1765272d59 100644 --- a/lib/proxy/peer/server_test.go +++ b/lib/proxy/peer/server_test.go @@ -34,7 +34,7 @@ func TestServerTLS(t *testing.T) { ca2 := newSelfSignedCA(t) // trusted certificates with proxy roles. - client1 := setupClient(t, ca1, ca1, types.RoleProxy) + client1 := setupClient(t, ca1, newAtomicCA(ca1), types.RoleProxy) _, serverDef1 := setupServer(t, "s1", ca1, ca1, types.RoleProxy) err := client1.updateConnections([]types.Server{serverDef1}) require.NoError(t, err) @@ -44,7 +44,7 @@ func TestServerTLS(t *testing.T) { stream.Close() // trusted certificates with incorrect server role. - client2 := setupClient(t, ca1, ca1, types.RoleNode) + client2 := setupClient(t, ca1, newAtomicCA(ca1), types.RoleNode) _, serverDef2 := setupServer(t, "s2", ca1, ca1, types.RoleProxy) err = client2.updateConnections([]types.Server{serverDef2}) require.NoError(t, err) // connection succeeds but is in transient failure state @@ -52,7 +52,7 @@ func TestServerTLS(t *testing.T) { require.Error(t, err) // certificates with correct role from different CAs - client3 := setupClient(t, ca1, ca2, types.RoleProxy) + client3 := setupClient(t, ca1, newAtomicCA(ca2), types.RoleProxy) _, serverDef3 := setupServer(t, "s3", ca2, ca1, types.RoleProxy) err = client3.updateConnections([]types.Server{serverDef3}) require.NoError(t, err) diff --git a/lib/reversetunnel/remotesite.go b/lib/reversetunnel/remotesite.go index 30f35a3bf6a9d..8e8b7e4c3fe79 100644 --- a/lib/reversetunnel/remotesite.go +++ b/lib/reversetunnel/remotesite.go @@ -20,6 +20,8 @@ package reversetunnel import ( "context" + "crypto/tls" + "crypto/x509" "fmt" "net" "sync" @@ -106,36 +108,45 @@ func (s *remoteSite) getRemoteClient() (authclient.ClientI, bool, error) { if err != nil { return nil, false, trace.Wrap(err) } - keys := ca.GetTrustedTLSKeyPairs() - + if len(ca.GetTrustedTLSKeyPairs()) == 0 { + return nil, false, trace.BadParameter("no TLS keys found") + } // The fact that cluster has keys to remote CA means that the key exchange // has completed. - if len(keys) != 0 { - s.logger.Debug("Using TLS client to remote cluster.") - pool, err := services.CertPool(ca) - if err != nil { - return nil, false, trace.Wrap(err) - } - tlsConfig := s.srv.ClientTLS.Clone() - tlsConfig.RootCAs = pool - // encode the name of this cluster to identify this cluster, - // connecting to the remote one (it is used to find the right certificate - // authority to verify) - tlsConfig.ServerName = apiutils.EncodeClusterName(s.srv.ClusterName) - clt, err := authclient.NewClient(client.Config{ - Dialer: client.ContextDialerFunc(s.authServerContextDialer), - Credentials: []client.Credentials{ - client.LoadTLS(tlsConfig), - }, - CircuitBreakerConfig: s.srv.CircuitBreakerConfig, - }) + + s.logger.Debug("Using TLS client to remote cluster.") + tlsConfig := utils.TLSConfig(s.srv.ClientTLSCipherSuites) + // encode the name of this cluster to identify this cluster, + // connecting to the remote one (it is used to find the right certificate + // authority to verify) + tlsConfig.ServerName = apiutils.EncodeClusterName(s.srv.ClusterName) + tlsConfig.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) { + tlsCert, err := s.srv.GetClientTLSCertificate() if err != nil { - return nil, false, trace.Wrap(err) + return nil, trace.Wrap(err) } - return clt, false, nil + return tlsCert, nil } + tlsConfig.InsecureSkipVerify = true + tlsConfig.VerifyConnection = utils.VerifyConnectionWithRoots(func() (*x509.CertPool, error) { + pool, _, err := authclient.ClientCertPool(s.ctx, s.srv.localAccessPoint, s.domainName, types.HostCA) + if err != nil { + return nil, trace.Wrap(err) + } + return pool, nil + }) - return nil, false, trace.BadParameter("no TLS keys found") + clt, err := authclient.NewClient(client.Config{ + Dialer: client.ContextDialerFunc(s.authServerContextDialer), + Credentials: []client.Credentials{ + client.LoadTLS(tlsConfig), + }, + CircuitBreakerConfig: s.srv.CircuitBreakerConfig, + }) + if err != nil { + return nil, false, trace.Wrap(err) + } + return clt, false, nil } func (s *remoteSite) authServerContextDialer(ctx context.Context, network, address string) (net.Conn, error) { diff --git a/lib/reversetunnel/srv.go b/lib/reversetunnel/srv.go index 260c20ebe61cb..d4557a2c75922 100644 --- a/lib/reversetunnel/srv.go +++ b/lib/reversetunnel/srv.go @@ -20,7 +20,6 @@ package reversetunnel import ( "context" - "crypto/tls" "fmt" "io" "net" @@ -131,9 +130,12 @@ type Config struct { ID string // ClusterName is a name of this cluster ClusterName string - // ClientTLS is a TLS config associated with this proxy - // used to connect to remote auth servers on remote clusters - ClientTLS *tls.Config + // ClientTLSCipherSuites optionally contains a list of TLS ciphersuites to + // use when connecting to other clusters. + ClientTLSCipherSuites []uint16 + // GetClientTLSCertificate returns a TLS certificate to use when connecting + // to other clusters. + GetClientTLSCertificate utils.GetCertificateFunc // Listener is a listener address for reverse tunnel server Listener net.Listener // HostSigners is a list of host signers @@ -226,8 +228,8 @@ func (cfg *Config) CheckAndSetDefaults() error { if cfg.ClusterName == "" { return trace.BadParameter("missing parameter ClusterName") } - if cfg.ClientTLS == nil { - return trace.BadParameter("missing parameter ClientTLS") + if cfg.GetClientTLSCertificate == nil { + return trace.BadParameter("missing parameter GetClientTLSCertificate") } if cfg.Listener == nil { return trace.BadParameter("missing parameter Listener") diff --git a/lib/secretsscanner/reporter/env_test.go b/lib/secretsscanner/reporter/env_test.go new file mode 100644 index 0000000000000..04ffebb5c6711 --- /dev/null +++ b/lib/secretsscanner/reporter/env_test.go @@ -0,0 +1,199 @@ +/* + * 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 reporter_test + +import ( + "errors" + "io" + "net" + "testing" + + "github.com/gravitational/trace" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + + accessgraphsecretsv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/accessgraph/v1" + devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1" + dttestenv "github.com/gravitational/teleport/lib/devicetrust/testenv" + "github.com/gravitational/teleport/lib/fixtures" +) + +type env struct { + secretsScannerAddr string + service *serviceFake +} + +type opts struct { + device *device + preReconcileError error +} + +type device struct { + device dttestenv.FakeDevice + id string +} + +type option func(*opts) + +func withDevice(deviceID string, dev dttestenv.FakeDevice) option { + return func(o *opts) { + o.device = &device{ + device: dev, + id: deviceID, + } + } +} + +func withPreReconcileError(err error) option { + return func(o *opts) { + o.preReconcileError = err + } +} + +func setup(t *testing.T, ops ...option) env { + t.Helper() + + o := opts{} + for _, op := range ops { + op(&o) + } + + var opts []dttestenv.Opt + if o.device != nil { + dev, pubKey, err := dttestenv.CreateEnrolledDevice(o.device.id, o.device.device) + require.NoError(t, err) + opts = append(opts, dttestenv.WithPreEnrolledDevice(dev, pubKey)) + } + dtFakeSvc, err := dttestenv.New(opts...) + require.NoError(t, err) + t.Cleanup(func() { + err := dtFakeSvc.Close() + assert.NoError(t, err) + }) + + svc := newServiceFake(dtFakeSvc.Service) + svc.preReconcileError = o.preReconcileError + + tlsConfig, err := fixtures.LocalTLSConfig() + require.NoError(t, err) + + grpcServer := grpc.NewServer( + grpc.Creds( + credentials.NewTLS(tlsConfig.TLS), + ), + ) + accessgraphsecretsv1pb.RegisterSecretsScannerServiceServer(grpcServer, svc) + + lis, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + go func() { + err := grpcServer.Serve(lis) + assert.NoError(t, err) + }() + t.Cleanup(func() { + grpcServer.Stop() + _ = lis.Close() + }) + + return env{ + service: svc, + secretsScannerAddr: lis.Addr().String(), + } +} + +func newServiceFake(deviceTrustSvc *dttestenv.FakeDeviceService) *serviceFake { + return &serviceFake{ + deviceTrustSvc: deviceTrustSvc, + } +} + +type serviceFake struct { + accessgraphsecretsv1pb.UnimplementedSecretsScannerServiceServer + privateKeysReported []*accessgraphsecretsv1pb.PrivateKey + deviceTrustSvc *dttestenv.FakeDeviceService + preReconcileError error +} + +func (s *serviceFake) ReportSecrets(in accessgraphsecretsv1pb.SecretsScannerService_ReportSecretsServer) error { + // Step 1. Assert the device. + if _, err := s.deviceTrustSvc.AssertDevice(in.Context(), streamAdapter{stream: in}); err != nil { + return trace.Wrap(err) + } + // Step 2. Collect the private keys into a temporary slice. + var collectedKeys []*accessgraphsecretsv1pb.PrivateKey + for { + msg, err := in.Recv() + // Step 4. When the client closes his side of the stream, we break the loop + // and collect the private keys. + if errors.Is(err, io.EOF) { + break + } else if err != nil { + return trace.Wrap(err) + } + + if msg.GetPrivateKeys() == nil { + return trace.BadParameter("unexpected assert request payload: %T", msg.GetPayload()) + } + // Step 3. Collect the private keys into a temporary slice. + collectedKeys = append(collectedKeys, msg.GetPrivateKeys().GetKeys()...) + + } + + if s.preReconcileError != nil { + return s.preReconcileError + } + + // Step 5. Store the collected private keys. + // This only happens when the client closes his side of the stream. + s.privateKeysReported = collectedKeys + return nil +} + +// streamAdapter is a helper struct that adapts the [accessgraphsecretsv1pb.SecretsScannerService_ReportSecretsServer] +// stream to the device trust assertion stream [assertserver.AssertDeviceServerStream]. +// This is needed because we need to extract the [*devicepb.AssertDeviceRequest] from the stream +// and return the [*devicepb.AssertDeviceResponse] to the stream. +type streamAdapter struct { + stream accessgraphsecretsv1pb.SecretsScannerService_ReportSecretsServer +} + +func (s streamAdapter) Send(rsp *devicepb.AssertDeviceResponse) error { + msg := &accessgraphsecretsv1pb.ReportSecretsResponse{ + Payload: &accessgraphsecretsv1pb.ReportSecretsResponse_DeviceAssertion{ + DeviceAssertion: rsp, + }, + } + err := s.stream.Send(msg) + return trace.Wrap(err) +} + +func (s streamAdapter) Recv() (*devicepb.AssertDeviceRequest, error) { + msg, err := s.stream.Recv() + if err != nil { + return nil, trace.Wrap(err) + } + + if msg.GetDeviceAssertion() == nil { + return nil, trace.BadParameter("unexpected assert request payload: %T", msg.GetPayload()) + } + + return msg.GetDeviceAssertion(), nil +} diff --git a/lib/secretsscanner/reporter/report.go b/lib/secretsscanner/reporter/report.go new file mode 100644 index 0000000000000..22dd8c22b9d3b --- /dev/null +++ b/lib/secretsscanner/reporter/report.go @@ -0,0 +1,195 @@ +/* + * 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 reporter + +import ( + "context" + "errors" + "io" + "log/slog" + + "github.com/gravitational/trace" + + accessgraphsecretsv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/accessgraph/v1" + devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1" + dtassert "github.com/gravitational/teleport/lib/devicetrust/assert" + secretsscannerclient "github.com/gravitational/teleport/lib/secretsscanner/client" +) + +// AssertCeremonyBuilderFunc is a function that builds the device authentication ceremony. +type AssertCeremonyBuilderFunc func() (*dtassert.Ceremony, error) + +// Config specifies the configuration for the reporter. +type Config struct { + // Client is a client for the SecretsScannerService. + Client secretsscannerclient.Client + // Log is the logger. + Log *slog.Logger + // BatchSize is the number of secrets to send in a single batch. Defaults to [defaultBatchSize] if not set. + BatchSize int + // AssertCeremonyBuilder is the device authentication ceremony builder. + // If not set, the default device authentication ceremony will be used. + // Used for testing, avoid in production code. + AssertCeremonyBuilder AssertCeremonyBuilderFunc +} + +// Reporter reports secrets to the Teleport Proxy. +type Reporter struct { + client secretsscannerclient.Client + log *slog.Logger + batchSize int + assertCeremonyBuilder AssertCeremonyBuilderFunc +} + +// New creates a new reporter instance. +func New(cfg Config) (*Reporter, error) { + if cfg.Client == nil { + return nil, trace.BadParameter("missing client") + } + if cfg.Log == nil { + cfg.Log = slog.Default() + } + if cfg.BatchSize == 0 { + const defaultBatchSize = 100 + cfg.BatchSize = defaultBatchSize + } + if cfg.AssertCeremonyBuilder == nil { + cfg.AssertCeremonyBuilder = func() (*dtassert.Ceremony, error) { + return dtassert.NewCeremony() + } + } + return &Reporter{ + client: cfg.Client, + log: cfg.Log, + batchSize: cfg.BatchSize, + assertCeremonyBuilder: cfg.AssertCeremonyBuilder, + }, nil +} + +// ReportPrivateKeys reports the private keys to the Teleport server. +// This function performs the following steps: +// 1. Create a new gRPC client to the Teleport Proxy. +// 2. Run the device assertion ceremony. +// 3. Report the private keys to the Teleport cluster. +// 4. Wait for the server to acknowledge the report. +func (r *Reporter) ReportPrivateKeys(ctx context.Context, pks []*accessgraphsecretsv1pb.PrivateKey) error { + + stream, err := r.client.ReportSecrets(ctx) + if err != nil { + return trace.Wrap(err, "failed to create client") + } + + if err := r.runAssertionCeremony(ctx, stream); err != nil { + return trace.Wrap(err, "failed to run assertion ceremony") + } + + if err := r.reportPrivateKeys(stream, pks); err != nil { + return trace.Wrap(err, "failed to report private keys") + } + + return trace.Wrap(r.terminateAndWaitAcknowledge(stream), "server failed to acknowledge the report") +} + +// runAssertionCeremony runs the device assertion ceremony. +func (r *Reporter) runAssertionCeremony(ctx context.Context, stream accessgraphsecretsv1pb.SecretsScannerService_ReportSecretsClient) error { + // Create a new device authentication ceremony. + assertCeremony, err := r.assertCeremonyBuilder() + if err != nil { + return trace.Wrap(err, "failed to create assertCeremony") + } + + // Run the device authentication ceremony. + // If successful, the device will be authenticated and the device can report its secrets. + err = assertCeremony.Run( + ctx, + reportToAssertStreamAdapter{stream}, + ) + return trace.Wrap(err, "failed to run device authentication ceremony") +} + +// reportPrivateKeys reports the private keys to the Teleport server in batches of size [r.batchSize] using the given stream. +func (r *Reporter) reportPrivateKeys(stream accessgraphsecretsv1pb.SecretsScannerService_ReportSecretsClient, privateKeys []*accessgraphsecretsv1pb.PrivateKey) error { + batchSize := r.batchSize + for i := 0; len(privateKeys) > i; i += batchSize { + start := i + end := i + batchSize + if end > len(privateKeys) { + end = len(privateKeys) + } + if err := stream.Send(&accessgraphsecretsv1pb.ReportSecretsRequest{ + Payload: &accessgraphsecretsv1pb.ReportSecretsRequest_PrivateKeys{ + PrivateKeys: &accessgraphsecretsv1pb.ReportPrivateKeys{ + Keys: privateKeys[start:end], + }, + }, + }); err != nil { + return trace.Wrap(err, "failed to send private keys") + } + } + return nil +} + +// terminateAndWaitAcknowledge terminates the client side of the stream and waits for the server to acknowledge the report. +func (r *Reporter) terminateAndWaitAcknowledge(stream accessgraphsecretsv1pb.SecretsScannerService_ReportSecretsClient) error { + // Inform the server that there are no more private keys to report. + if err := stream.CloseSend(); err != nil { + return trace.Wrap(err, "failed to close send") + } + + // Wait for the server to acknowledge the report. + if _, err := stream.Recv(); err != nil && !errors.Is(err, io.EOF) { + return trace.Wrap(err, "error closing the stream") + } + return nil +} + +// reportToAssertStreamAdapter is a wrapper for the [accessgraphsecretsv1pb.SecretsScannerService_ReportSecretsClient] that implements the +// [assert.AssertDeviceClientStream] interface. +// +// This adapter allows the [accessgraphsecretsv1pb.SecretsScannerService_ReportSecretsClient] to be used with the [assert.AssertDeviceClientStream] +// interface, which is essential for the [assert.Ceremony] in executing the device authentication process. It handles the extraction and insertion +// of device assertion messages from and into the [accessgraphsecretsv1pb.ReportSecretsRequest] and [accessgraphsecretsv1pb.ReportSecretsResponse] messages. +type reportToAssertStreamAdapter struct { + stream accessgraphsecretsv1pb.SecretsScannerService_ReportSecretsClient +} + +func (s reportToAssertStreamAdapter) Send(request *devicepb.AssertDeviceRequest) error { + return trace.Wrap( + s.stream.Send( + &accessgraphsecretsv1pb.ReportSecretsRequest{ + Payload: &accessgraphsecretsv1pb.ReportSecretsRequest_DeviceAssertion{ + DeviceAssertion: request, + }, + }, + ), + ) +} + +func (s reportToAssertStreamAdapter) Recv() (*devicepb.AssertDeviceResponse, error) { + in, err := s.stream.Recv() + if err != nil { + return nil, trace.Wrap(err) + } + + if in.GetDeviceAssertion() == nil { + return nil, trace.BadParameter("unsupported response type: expected DeviceAssertion, got %T", in.Payload) + } + + return in.GetDeviceAssertion(), nil +} diff --git a/lib/secretsscanner/reporter/report_test.go b/lib/secretsscanner/reporter/report_test.go new file mode 100644 index 0000000000000..76c2d661cd116 --- /dev/null +++ b/lib/secretsscanner/reporter/report_test.go @@ -0,0 +1,157 @@ +/* + * 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 reporter_test + +import ( + "context" + "errors" + "log/slog" + "sort" + "strconv" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/uuid" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/testing/protocmp" + + "github.com/gravitational/teleport/api/defaults" + accessgraphsecretsv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/accessgraph/v1" + devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1" + "github.com/gravitational/teleport/api/types/accessgraph" + dtassert "github.com/gravitational/teleport/lib/devicetrust/assert" + dtauthn "github.com/gravitational/teleport/lib/devicetrust/authn" + dttestenv "github.com/gravitational/teleport/lib/devicetrust/testenv" + secretsscannerclient "github.com/gravitational/teleport/lib/secretsscanner/client" + "github.com/gravitational/teleport/lib/secretsscanner/reporter" +) + +func TestReporter(t *testing.T) { + // disable TLS routing check for tests + t.Setenv(defaults.TLSRoutingConnUpgradeEnvVar, "false") + deviceID := uuid.NewString() + device, err := dttestenv.NewFakeMacOSDevice() + require.NoError(t, err) + + tests := []struct { + name string + preReconcileError error + assertErr require.ErrorAssertionFunc + report []*accessgraphsecretsv1pb.PrivateKey + want []*accessgraphsecretsv1pb.PrivateKey + }{ + { + name: "success", + report: newPrivateKeys(t, deviceID), + want: newPrivateKeys(t, deviceID), + assertErr: require.NoError, + }, + { + name: "pre-reconcile error", + preReconcileError: errors.New("pre-reconcile error"), + report: newPrivateKeys(t, deviceID), + assertErr: func(t require.TestingT, err error, _ ...any) { + require.ErrorContains(t, err, "pre-reconcile error") + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + e := setup( + t, + withDevice(deviceID, device), + withPreReconcileError(tt.preReconcileError), + ) + + ctx := context.Background() + + client, err := secretsscannerclient.NewSecretsScannerServiceClient(ctx, + secretsscannerclient.ClientConfig{ + ProxyServer: e.secretsScannerAddr, + Insecure: true, + }, + ) + require.NoError(t, err) + + r, err := reporter.New( + reporter.Config{ + Log: slog.Default(), + Client: client, + BatchSize: 1, /* batch size for tests */ + AssertCeremonyBuilder: func() (*dtassert.Ceremony, error) { + return dtassert.NewCeremony( + dtassert.WithNewAuthnCeremonyFunc( + func() *dtauthn.Ceremony { + return &dtauthn.Ceremony{ + GetDeviceCredential: func() (*devicepb.DeviceCredential, error) { + return device.GetDeviceCredential(), nil + }, + CollectDeviceData: device.CollectDeviceData, + SignChallenge: device.SignChallenge, + SolveTPMAuthnDeviceChallenge: device.SolveTPMAuthnDeviceChallenge, + GetDeviceOSType: device.GetDeviceOSType, + } + }, + ), + ) + }, + }, + ) + require.NoError(t, err) + + err = r.ReportPrivateKeys(ctx, tt.report) + tt.assertErr(t, err) + + got := e.service.privateKeysReported + sortPrivateKeys(got) + sortPrivateKeys(tt.want) + + diff := cmp.Diff(tt.want, got, protocmp.Transform()) + require.Empty(t, diff, "ReportPrivateKeys keys mismatch (-got +want)") + + }) + } +} + +func sortPrivateKeys(keys []*accessgraphsecretsv1pb.PrivateKey) { + sort.Slice(keys, func(i, j int) bool { + return keys[i].Metadata.Name < keys[j].Metadata.Name + }) +} + +func newPrivateKeys(t *testing.T, deviceID string) []*accessgraphsecretsv1pb.PrivateKey { + t.Helper() + var pks []*accessgraphsecretsv1pb.PrivateKey + for i := 0; i < 10; i++ { + pk, err := accessgraph.NewPrivateKey( + &accessgraphsecretsv1pb.PrivateKeySpec{ + PublicKeyFingerprint: "key" + strconv.Itoa(i), + DeviceId: deviceID, + PublicKeyMode: accessgraphsecretsv1pb.PublicKeyMode_PUBLIC_KEY_MODE_DERIVED, + }, + ) + require.NoError(t, err) + pks = append(pks, pk) + } + + return pks +} diff --git a/lib/secretsscanner/scaner/scan.go b/lib/secretsscanner/scaner/scan.go new file mode 100644 index 0000000000000..664b6fcef8e67 --- /dev/null +++ b/lib/secretsscanner/scaner/scan.go @@ -0,0 +1,298 @@ +/* + * 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 scanner + +import ( + "bytes" + "context" + "crypto/sha256" + "encoding/hex" + "errors" + "io" + "io/fs" + "log/slog" + "os" + "path/filepath" + + "github.com/gravitational/trace" + "golang.org/x/crypto/ssh" + + accessgraphsecretsv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/accessgraph/v1" + "github.com/gravitational/teleport/api/types/accessgraph" +) + +// Config specifies parameters for the scanner. +type Config struct { + // Dirs is a list of directories to scan. + Dirs []string + // SkipPaths is a list of paths to skip. + // It supports glob patterns (e.g. "/etc/*/"). + // Please refer to the [filepath.Match] documentation for more information. + SkipPaths []string + // Log is the logger. + Log *slog.Logger +} + +// New creates a new scanner. +func New(cfg Config) (*Scanner, error) { + if len(cfg.Dirs) == 0 { + return nil, trace.BadParameter("missing dirs") + } + if cfg.Log == nil { + cfg.Log = slog.Default() + } + + // expand the glob patterns in the skipPaths list. + // we expand the glob patterns here to avoid expanding them for each file during the scan. + // only the directories matched by the glob patterns will be skipped. + skippedPaths, err := expandSkipPaths(cfg.SkipPaths) + if err != nil { + return nil, trace.Wrap(err) + } + + return &Scanner{ + dirs: cfg.Dirs, + log: cfg.Log, + skippedPaths: skippedPaths, + }, nil +} + +// Scanner is a scanner that scans directories for secrets. +type Scanner struct { + dirs []string + log *slog.Logger + skippedPaths map[string]struct{} +} + +// ScanPrivateKeys scans directories for SSH private keys. +func (s *Scanner) ScanPrivateKeys(ctx context.Context, deviceID string) []SSHPrivateKey { + // privateKeys is a map of private keys found during the scan. + // The key is the path to the private key file and the value is the private key representation. + privateKeysMap := make(map[string]*accessgraphsecretsv1pb.PrivateKey) + for _, dir := range s.dirs { + s.findPrivateKeys(ctx, dir, deviceID, privateKeysMap) + } + + keys := make([]SSHPrivateKey, 0, len(privateKeysMap)) + for path, key := range privateKeysMap { + keys = append(keys, SSHPrivateKey{ + Path: path, + Key: key, + }) + } + return keys +} + +// SSHPrivateKey represents an SSH private key found during the scan. +type SSHPrivateKey struct { + // Path is the absolute path to the private key file. + Path string + // Key is the private key representation. + Key *accessgraphsecretsv1pb.PrivateKey +} + +// findPrivateKeys walks through all files in a directory and its subdirectories +// and checks if they are SSH private keys. +func (s *Scanner) findPrivateKeys(ctx context.Context, root, deviceID string, privateKeysMap map[string]*accessgraphsecretsv1pb.PrivateKey) { + logger := s.log.With("root", root) + + err := filepath.WalkDir(root, func(path string, info fs.DirEntry, err error) error { + if err != nil { + logger.DebugContext(ctx, "error walking directory", "path", path, "error", err) + return fs.SkipDir + } + if info.IsDir() { + if _, ok := s.skippedPaths[path]; ok { + logger.DebugContext(ctx, "skipping directory", "path", path) + return fs.SkipDir + } + return nil + } + + if _, ok := s.skippedPaths[path]; ok { + logger.DebugContext(ctx, "skipping file", "path", path) + return nil + } + + switch fileData, isKey, err := s.readFileIfSSHPrivateKey(ctx, path); { + case err != nil: + logger.DebugContext(ctx, "error reading file", "path", path, "error", err) + case isKey: + key, err := extractSSHKey(ctx, path, deviceID, fileData) + if err != nil { + logger.DebugContext(ctx, "error extracting private key", "path", path, "error", err) + } else { + privateKeysMap[path] = key + } + } + return nil + }) + + if err != nil { + logger.WarnContext(ctx, "error walking directory", "root", root, "error", err) + } +} + +var ( + supportedPrivateKeyHeaders = [][]byte{ + []byte("RSA PRIVATE KEY"), + []byte("PRIVATE KEY"), + []byte("EC PRIVATE KEY"), + []byte("DSA PRIVATE KEY"), + []byte("OPENSSH PRIVATE KEY"), + } +) + +// readFileIfSSHPrivateKey checks if a file is an OpenSSH private key +func (s *Scanner) readFileIfSSHPrivateKey(ctx context.Context, filePath string) ([]byte, bool, error) { + file, err := os.Open(filePath) + if err != nil { + return nil, false, err + } + defer func() { + if err = file.Close(); err != nil { + s.log.DebugContext(ctx, "failed to close file", "path", filePath, "error", err) + } + }() + + // read the first bytes of the file to check if it's an OpenSSH private key. + // 40 bytes is the maximum length of the header of an OpenSSH private key. + var buf [40]byte + n, err := file.Read(buf[:]) + if errors.Is(err, io.EOF) || n < len(buf) { + return nil, false, nil + } else if err != nil { + return nil, false, trace.Wrap(err, "failed to read file") + } + + isPrivateKey := false + for _, header := range supportedPrivateKeyHeaders { + if bytes.Contains(buf[:], header) { + isPrivateKey = true + break + } + } + if !isPrivateKey { + return nil, false, nil + } + + // read the entire file + data, err := io.ReadAll(file) + if err != nil { + return nil, false, trace.Wrap(err, "failed to read file") + } + return append(buf[:], data...), true, nil +} + +func extractSSHKey(ctx context.Context, path, deviceID string, fileData []byte) (*accessgraphsecretsv1pb.PrivateKey, error) { + logger := slog.Default().With("private_key_file", path, "device_id", deviceID) + + var publicKey ssh.PublicKey + var mode accessgraphsecretsv1pb.PublicKeyMode + var pme *ssh.PassphraseMissingError + switch pk, err := ssh.ParsePrivateKey(fileData); { + case errors.As(err, &pme): + if pme.PublicKey != nil { + // If the key is a OpenSSH private key whose public key is embedded in the header, it will return the public key. This is + // a special case for OpenSSH private keys that have the public key embedded in the header, for more information see + // OpenSSH's ssh key format: https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key + publicKey = pme.PublicKey + mode = accessgraphsecretsv1pb.PublicKeyMode_PUBLIC_KEY_MODE_DERIVED + break + } + const pubKeyFileSuffix = ".pub" + publicKey, mode = tryParsingPublicKeyFromPublicFilePath(ctx, logger, path+pubKeyFileSuffix) + case err != nil: + return nil, trace.Wrap(err) + default: + publicKey = pk.PublicKey() + mode = accessgraphsecretsv1pb.PublicKeyMode_PUBLIC_KEY_MODE_DERIVED + } + var fingerprint string + if publicKey != nil { + fingerprint = ssh.FingerprintSHA256(publicKey) + } + + key, err := accessgraph.NewPrivateKeyWithName( + privateKeyNameGen(path, deviceID, fingerprint), + &accessgraphsecretsv1pb.PrivateKeySpec{ + PublicKeyFingerprint: fingerprint, + DeviceId: deviceID, + PublicKeyMode: mode, + }, + ) + return key, trace.Wrap(err) +} + +// tryParsingPublicKeyFromPublicFilePath tries to read the public key from the public key file if the private key is password protected. +// If the public key file doesn't exist, it will return mode accessgraphsecretsv1pb.PublicKeyMode_PUBLIC_KEY_MODE_PROTECTED +// identifying that the private key is password protected and the public key could not be extracted. +func tryParsingPublicKeyFromPublicFilePath(ctx context.Context, logger *slog.Logger, pubPath string) (ssh.PublicKey, accessgraphsecretsv1pb.PublicKeyMode) { + logger = logger.With("public_key_file", pubPath) + logger.DebugContext(ctx, "PrivateKey is password protected. Fallback to public key file.") + + pubData, err := os.ReadFile(pubPath) + if err != nil { + logger.DebugContext(ctx, "Unable to read public key file.", "err", err) + return nil, accessgraphsecretsv1pb.PublicKeyMode_PUBLIC_KEY_MODE_PROTECTED + } + + logger.DebugContext(ctx, "Trying to parse public key as authorized key data.") + pub, _, _, _, err := ssh.ParseAuthorizedKey(pubData) + if err == nil { + return pub, accessgraphsecretsv1pb.PublicKeyMode_PUBLIC_KEY_MODE_PUB_FILE + } + logger.DebugContext(ctx, "Unable to parse ssh public key file.", "err", err) + + logger.DebugContext(ctx, "Trying to parse public key directly.") + + pub, err = ssh.ParsePublicKey(pubData) + if err == nil { + return pub, accessgraphsecretsv1pb.PublicKeyMode_PUBLIC_KEY_MODE_PUB_FILE + } + + logger.DebugContext(ctx, "Unable to parse ssh public key file.", "err", err) + + return nil, accessgraphsecretsv1pb.PublicKeyMode_PUBLIC_KEY_MODE_PROTECTED + +} + +func privateKeyNameGen(path, deviceID, fingerprint string) string { + sha := sha256.New() + sha.Write([]byte(path)) + sha.Write([]byte(deviceID)) + sha.Write([]byte(fingerprint)) + return hex.EncodeToString(sha.Sum(nil)) +} + +// expandSkipPaths expands the glob patterns in the skipPaths list and returns a set of the +// paths matched by the glob patterns to be skipped. +func expandSkipPaths(skipPaths []string) (map[string]struct{}, error) { + set := make(map[string]struct{}) + for _, glob := range skipPaths { + matches, err := filepath.Glob(glob) + if err != nil { + return nil, trace.Wrap(err, "glob pattern %q is invalid", glob) + } + for _, match := range matches { + set[match] = struct{}{} + } + } + return set, nil +} diff --git a/lib/secretsscanner/scaner/scan_test.go b/lib/secretsscanner/scaner/scan_test.go new file mode 100644 index 0000000000000..df82e08eb0ea7 --- /dev/null +++ b/lib/secretsscanner/scaner/scan_test.go @@ -0,0 +1,237 @@ +/* + * 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 scanner + +import ( + "context" + "os" + "path/filepath" + "sort" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/uuid" + "github.com/stretchr/testify/require" + "golang.org/x/crypto/ssh" + "google.golang.org/protobuf/testing/protocmp" + + accessgraphsecretsv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/accessgraph/v1" + "github.com/gravitational/teleport/api/types/accessgraph" + scantestdata "github.com/gravitational/teleport/lib/secretsscanner/scaner/testdata" +) + +var ( + deviceID = uuid.NewString() +) + +func TestNewScanner(t *testing.T) { + tests := []struct { + name string + keysGen func(t *testing.T, path string) []*accessgraphsecretsv1pb.PrivateKey + skipTestDir bool + assertResult func(t *testing.T, got []*accessgraphsecretsv1pb.PrivateKey) + }{ + { + name: "encrypted keys", + keysGen: writeEncryptedKeys, + }, + { + name: "unencrypted keys", + keysGen: writeUnEncryptedKeys, + }, + { + name: "encryptedKey without public key file", + keysGen: writeEncryptedKeyWithoutPubFile, + }, + { + name: "invalid keys", + keysGen: writeInvalidKeys, + }, + { + name: "skip test dir keys", + keysGen: writeUnEncryptedKeys, + skipTestDir: true, + assertResult: func(t *testing.T, got []*accessgraphsecretsv1pb.PrivateKey) { + require.Empty(t, got, "ScanPrivateKeys with skip test dir should return empty keys") + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + dir := t.TempDir() + + expect := tt.keysGen(t, dir) + + var skipPaths []string + if tt.skipTestDir { + // skip the test directory. + skipPaths = []string{filepath.Join(dir, "*")} + // the expected keys should be nil since the test directory is skipped. + expect = nil + } + + s, err := New(Config{ + Dirs: []string{dir}, + SkipPaths: skipPaths, + }) + require.NoError(t, err) + + keys := s.ScanPrivateKeys(context.Background(), deviceID) + var got []*accessgraphsecretsv1pb.PrivateKey + for _, key := range keys { + got = append(got, key.Key) + } + + // Sort the keys by name for comparison. + sortPrivateKeys(expect) + sortPrivateKeys(got) + + if tt.assertResult != nil { + tt.assertResult(t, got) + } + + diff := cmp.Diff(expect, got, protocmp.Transform()) + require.Empty(t, diff, "ScanPrivateKeys keys mismatch (-got +want)") + }) + } +} + +func sortPrivateKeys(keys []*accessgraphsecretsv1pb.PrivateKey) { + sort.Slice(keys, func(i, j int) bool { + return keys[i].Metadata.Name < keys[j].Metadata.Name + }) +} + +func writeEncryptedKeys(t *testing.T, dir string) []*accessgraphsecretsv1pb.PrivateKey { + t.Helper() + var expectedKeys []*accessgraphsecretsv1pb.PrivateKey + // Write encrypted keys to the directory. + for _, key := range scantestdata.PEMEncryptedKeys { + err := os.Mkdir(filepath.Join(dir, key.Name), 0o777) + require.NoError(t, err) + + filePath := filepath.Join(dir, key.Name, key.Name) + err = os.WriteFile(filePath, key.PEMBytes, 0o666) + require.NoError(t, err) + + s, err := ssh.ParsePrivateKeyWithPassphrase(key.PEMBytes, []byte(key.EncryptionKey)) + require.NoError(t, err) + + if !key.IncludesPublicKey { + pubFilePath := filePath + ".pub" + authorizedKeyBytes := ssh.MarshalAuthorizedKey(s.PublicKey()) + require.NoError(t, os.WriteFile(pubFilePath, authorizedKeyBytes, 0o666)) + } + + fingerprint := ssh.FingerprintSHA256(s.PublicKey()) + + mode := accessgraphsecretsv1pb.PublicKeyMode_PUBLIC_KEY_MODE_DERIVED + if !key.IncludesPublicKey { + mode = accessgraphsecretsv1pb.PublicKeyMode_PUBLIC_KEY_MODE_PUB_FILE + } + + key, err := accessgraph.NewPrivateKeyWithName( + privateKeyNameGen(filePath, deviceID, fingerprint), + &accessgraphsecretsv1pb.PrivateKeySpec{ + PublicKeyFingerprint: fingerprint, + DeviceId: deviceID, + PublicKeyMode: mode, + }, + ) + require.NoError(t, err) + + expectedKeys = append(expectedKeys, key) + } + + return expectedKeys +} + +func writeUnEncryptedKeys(t *testing.T, dir string) []*accessgraphsecretsv1pb.PrivateKey { + t.Helper() + var expectedKeys []*accessgraphsecretsv1pb.PrivateKey + + for name, key := range scantestdata.PEMBytes { + err := os.Mkdir(filepath.Join(dir, name), 0o777) + require.NoError(t, err) + + filePath := filepath.Join(dir, name, name) + err = os.WriteFile(filePath, key, 0o666) + require.NoError(t, err) + + s, err := ssh.ParsePrivateKey(key) + require.NoError(t, err) + + fingerprint := ssh.FingerprintSHA256(s.PublicKey()) + + key, err := accessgraph.NewPrivateKeyWithName( + privateKeyNameGen(filePath, deviceID, fingerprint), + &accessgraphsecretsv1pb.PrivateKeySpec{ + PublicKeyFingerprint: fingerprint, + DeviceId: deviceID, + PublicKeyMode: accessgraphsecretsv1pb.PublicKeyMode_PUBLIC_KEY_MODE_DERIVED, + }, + ) + require.NoError(t, err) + + expectedKeys = append(expectedKeys, key) + } + + return expectedKeys +} + +func writeEncryptedKeyWithoutPubFile(t *testing.T, dir string) []*accessgraphsecretsv1pb.PrivateKey { + t.Helper() + + // Write encrypted keys to the directory. + rawKey := scantestdata.PEMEncryptedKeys[0] + err := os.Mkdir(filepath.Join(dir, rawKey.Name), 0o777) + require.NoError(t, err) + + filePath := filepath.Join(dir, rawKey.Name, rawKey.Name) + err = os.WriteFile(filePath, rawKey.PEMBytes, 0o666) + require.NoError(t, err) + + key, err := accessgraph.NewPrivateKeyWithName( + privateKeyNameGen(filePath, deviceID, ""), + &accessgraphsecretsv1pb.PrivateKeySpec{ + PublicKeyFingerprint: "", + DeviceId: deviceID, + PublicKeyMode: accessgraphsecretsv1pb.PublicKeyMode_PUBLIC_KEY_MODE_PROTECTED, + }, + ) + require.NoError(t, err) + + return []*accessgraphsecretsv1pb.PrivateKey{key} +} + +func writeInvalidKeys(t *testing.T, dir string) []*accessgraphsecretsv1pb.PrivateKey { + t.Helper() + + // Write invalid keys to the directory. + for path, keyBytes := range scantestdata.InvalidKeysBytes { + err := os.Mkdir(filepath.Join(dir, path), 0o777) + require.NoError(t, err) + + filePath := filepath.Join(dir, path, path) + err = os.WriteFile(filePath, keyBytes, 0o666) + require.NoError(t, err) + } + + return nil +} diff --git a/lib/secretsscanner/scaner/testdata/invalid_keys.go b/lib/secretsscanner/scaner/testdata/invalid_keys.go new file mode 100644 index 0000000000000..b2e1b18732770 --- /dev/null +++ b/lib/secretsscanner/scaner/testdata/invalid_keys.go @@ -0,0 +1,50 @@ +/* + * 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 testdata + +// InvalidKeysBytes is a map of invalid keys to their byte representation. +var InvalidKeysBytes = map[string][]byte{ + "short-file": []byte("short file"), + + "empty-file": []byte(""), + + "invalid-key": []byte(`-----BEGIN PRIVATE +KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQ7z7z7z7z7z7z +-----END OPENSSH PRIVATE KEY----- +`), + + "invalid-key-valid-headers": []byte( + `-----BEGIN OPENSSH PRIVATE KEY----- +trash +-----END OPENSSH PRIVATE KEY----- +`), + + "invalid-key-invalid-header": []byte( + `abcefg-----BEGIN OPENSSH PRIVATE KEY----- +-----END OPENSSH PRIVATE KEY----- +`), + + "valid-key-not-supported-header": []byte(`-----BEGIN RANDOM PRIVATE KEY----- +MHcCAQEEINGWx0zo6fhJ/0EAfrPzVFyFC9s18lBt3cRoEDhS3ARooAoGCCqGSM49 +AwEHoUQDQgAEi9Hdw6KvZcWxfg2IDhA7UkpDtzzt6ZqJXSsFdLd+Kx4S3Sx4cVO+ +6/ZOXRnPmNAlLUqjShUsUBBngG0u2fqEqA== +-----END EC PRIVATE KEY----- +`), +} diff --git a/lib/secretsscanner/scaner/testdata/ssh_keys.go b/lib/secretsscanner/scaner/testdata/ssh_keys.go new file mode 100644 index 0000000000000..ff35441f77581 --- /dev/null +++ b/lib/secretsscanner/scaner/testdata/ssh_keys.go @@ -0,0 +1,290 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// forked from golang.org/x/crypto@v0.24.0/ssh/testdata/keys.go + +package testdata + +var PEMBytes = map[string][]byte{ + "dsa": []byte(`-----BEGIN DSA PRIVATE KEY----- +MIIBuwIBAAKBgQD6PDSEyXiI9jfNs97WuM46MSDCYlOqWw80ajN16AohtBncs1YB +lHk//dQOvCYOsYaE+gNix2jtoRjwXhDsc25/IqQbU1ahb7mB8/rsaILRGIbA5WH3 +EgFtJmXFovDz3if6F6TzvhFpHgJRmLYVR8cqsezL3hEZOvvs2iH7MorkxwIVAJHD +nD82+lxh2fb4PMsIiaXudAsBAoGAQRf7Q/iaPRn43ZquUhd6WwvirqUj+tkIu6eV +2nZWYmXLlqFQKEy4Tejl7Wkyzr2OSYvbXLzo7TNxLKoWor6ips0phYPPMyXld14r +juhT24CrhOzuLMhDduMDi032wDIZG4Y+K7ElU8Oufn8Sj5Wge8r6ANmmVgmFfynr +FhdYCngCgYEA3ucGJ93/Mx4q4eKRDxcWD3QzWyqpbRVRRV1Vmih9Ha/qC994nJFz +DQIdjxDIT2Rk2AGzMqFEB68Zc3O+Wcsmz5eWWzEwFxaTwOGWTyDqsDRLm3fD+QYj +nOwuxb0Kce+gWI8voWcqC9cyRm09jGzu2Ab3Bhtpg8JJ8L7gS3MRZK4CFEx4UAfY +Fmsr0W6fHB9nhS4/UXM8 +-----END DSA PRIVATE KEY----- +`), + "ecdsa": []byte(`-----BEGIN EC PRIVATE KEY----- +MHcCAQEEINGWx0zo6fhJ/0EAfrPzVFyFC9s18lBt3cRoEDhS3ARooAoGCCqGSM49 +AwEHoUQDQgAEi9Hdw6KvZcWxfg2IDhA7UkpDtzzt6ZqJXSsFdLd+Kx4S3Sx4cVO+ +6/ZOXRnPmNAlLUqjShUsUBBngG0u2fqEqA== +-----END EC PRIVATE KEY----- +`), + "ecdsap256": []byte(`-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIAPCE25zK0PQSnsgVcEbM1mbKTASH4pqb5QJajplDwDZoAoGCCqGSM49 +AwEHoUQDQgAEWy8TxGcIHRh5XGpO4dFVfDjeNY+VkgubQrf/eyFJZHxAn1SKraXU +qJUjTKj1z622OxYtJ5P7s9CfAEVsTzLCzg== +-----END EC PRIVATE KEY----- +`), + "ecdsap384": []byte(`-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDBWfSnMuNKq8J9rQLzzEkx3KAoEohSXqhE/4CdjEYtoU2i22HW80DDS +qQhYNHRAduygBwYFK4EEACKhZANiAAQWaDMAd0HUd8ZiXCX7mYDDnC54gwH/nG43 +VhCUEYmF7HMZm/B9Yn3GjFk3qYEDEvuF/52+NvUKBKKaLbh32AWxMv0ibcoba4cz +hL9+hWYhUD9XIUlzMWiZ2y6eBE9PdRI= +-----END EC PRIVATE KEY----- +`), + "ecdsap521": []byte(`-----BEGIN EC PRIVATE KEY----- +MIHcAgEBBEIBrkYpQcy8KTVHNiAkjlFZwee90224Bu6wz94R4OBo+Ts0eoAQG7SF +iaygEDMUbx6kTgXTBcKZ0jrWPKakayNZ/kigBwYFK4EEACOhgYkDgYYABADFuvLV +UoaCDGHcw5uNfdRIsvaLKuWSpLsl48eWGZAwdNG432GDVKduO+pceuE+8XzcyJb+ +uMv+D2b11Q/LQUcHJwE6fqbm8m3EtDKPsoKs0u/XUJb0JsH4J8lkZzbUTjvGYamn +FFlRjzoB3Oxu8UQgb+MWPedtH9XYBbg9biz4jJLkXQ== +-----END EC PRIVATE KEY----- +`), + "rsa": []byte(`-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAnuozKMtcQkIImZGSe4IujS4+Lkas9jmlBivziWGU3waivkpU +vYspgJbh7vSvnHOPtKscdIJ+3UUyViDUoM73GumsmHvfeRCoA9YROZK4fQR9G0a1 +wfoRqsrJXGToCzTvr/I2KIwpUG0bRE9rUvsW+JN9xgri+cIJWtu/dGYDkILO4bkF +IxtEvHNVvhGLenyOHFhPw3hAZ7/bKq8kvKzm9D2zOllHe1wWncMkhVmEFF9Houeh +jbddmeIAAxBpRUFfzp1dD7503ADBlJdK306D4CeI4KIFiqE1VrmfcMgP8fti0S0b +4JtmvevYoPd+/wB9ItFqvhc6nyuxF0PfWH+SvwIDAQABAoIBAQCCcpNeSFjKVvRC +Q1nwIrPd1njaec+fK0CIqWl3e2++B+9trwySrvp5gOGjyp2hGsd7Mf7gsQI81oF0 +a+y+uEXlhK3WWdDey0pwI7ft/7+LeDTOQCQRQBpijaXvPzGviVu7nWLRtARx7a41 +S9A4xL5dfI0BFYyuIpaVS8+EV/1TEJIbceZ5q5RBlARA1rc+nBvjygNzYdRq9Rao +yyehvnXZ7pQrATnwofPolbZNseW2Q9sRMmtm1E60XJJ433P7nFbxXtsMAYgxWSQc +V/92iRPYeD7sN/b7qulLEgC6e8el2gLGIB9aQyG7B6KFqloqvx/ymYs07+bWIQCU +6i9y7LABAoGBANKB2Rs0lF5c+gpEE3w0AWoxGyL04TZECtl2hQ/b2jc2n8hLqLhv +zNIKN+xzEP6j0ijXajjEMLiQH10qQ6+Plv8C7o8GJC1V/Oj4u4kbPqfVV1kSxuWm +FBjz6+c8VPbEBgXq5lCMEgC2Ii8XVRoyd3iSSOh+LIMBu/Br3JEsjJq/AoGBAMFC +CvODTiThrZo8v765dRSHrLOvB4jZOPrEKLWECLaQDQpuhgzZhyWm9zFfxGB+LWE9 +R9pU6ZCFPtPfd4cRZ9cezrp+lgdrqjcUX/2ZLLMk21WXFuRaw/4KTlKNsMY6lbAK +qVkUFWIZWaCQ8bdVCP48polipRNmN9mAHsIhIwgBAoGBAM6yn1qeS11I0GAKLlPT +wNvjsfCmIQmm0DxtqwRCbUevxD7pQ5cueCB51iW/ap2OgEqIEo4A3pIrOhDB8kpN +pQdrepFHh3hYqYicy5A6B1DHJAibbl+Krss9n5KjZA4VtpBS8al/kCHQtUomD/M0 +QKlMgnh/g/dzWXYegyqtYraDAoGAGbeBH5B8iJnjcR/eYDHrq5S2XZ7QANzvISeT +RzxPsIOQyK+WdQVJX7BNOqvExRZlUYhHFH2yKwIgLy+Qh0/Aora9ycFok4o3N2cl +suh8M0aXTVdyu2Z8qESU0ZV7TZWkL63rhSgQBGLdM2m2ULAnJzXI74VJ9D/o9K+A +6FJiiAECgYEAujJ/hKxVKEUvxwloGSDhKCUH86+7UOkb/EM2zZFrlPYAz1VcCwr3 +K14r8BtmLFXuLXOlACpoH0Wf4uia+t6n8m9JK3mvpJ7fempAsptP3AdZMQFe7xUm +SXEGQBYxcyS5Q+ncwWZuPgby5wJ9D4Fd6TQH+wwG52sFugt/fGxbPug= +-----END RSA PRIVATE KEY----- +`), + "pkcs8": []byte(`-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCitzS2KiRQTccf +VApb0mbPpo1lt29JjeLBYAehXHWfQ+w8sXpd8e04n/020spx1R94yg+v0NjXyh2R +NFXNBYdhNei33VJxUeKNlExaecvW2yxfuZqka+ZxT1aI8zrAsjh3Rwc6wayAJS4R +wZuzlDv4jZitWqwD+mb/22Zwq/WSs4YX5dUHDklfdWSVnoBfue8K/00n8f5yMTdJ +vFF0qAJwf9spPEHla0lYcozJk64CO5lRkqfLor4UnsXXOiA7aRIoaUSKa+rlhiqt +1EMGYiBjblPt4SwMelGGU2UfywPb4d85gpQ/s8SBARbpPxNVs2IbHDMwj70P3uZc +74M3c4VJAgMBAAECggEAFIzY3mziGzZHgMBncoNXMsCRORh6uKpvygZr0EhSHqRA +cMXlc3n7gNxL6aGjqc7F48Z5RrY0vMQtCcq3T2Z0W6WoV5hfMiqqV0E0h3S8ds1F +hG13h26NMyBXCILXl8Cqev4Afr45IBISCHIQTRTaoiCX+MTr1rDIU2YNQQumvzkz +fMw2XiFTFTgxAtJUAgKoTqLtm7/T+az7TKw+Hesgbx7yaJoMh9DWGBh4Y61DnIDA +fcxJboAfxxnFiXvdBVmzo72pCsRXrWOsjW6WxQmCKuXHvyB1FZTmMaEFNCGSJDa6 +U+OCzA3m65loAZAE7ffFHhYgssz/h9TBaOjKO0BX1QKBgQDZiCBvu+bFh9pEodcS +VxaI+ATlsYcmGdLtnZw5pxuEdr60iNWhpEcV6lGkbdiv5aL43QaGFDLagqeHI77b ++ITFbPPdCiYNaqlk6wyiXv4pdN7V683EDmGWSQlPeC9IhUilt2c+fChK2EB/XlkO +q8c3Vk1MsC6JOxDXNgJxylNpswKBgQC/fYBTb9iD+uM2n3SzJlct/ZlPaONKnNDR +pbTOdxBFHsu2VkfY858tfnEPkmSRX0yKmjHni6e8/qIzfzLwWBY4NmxhNZE5v+qJ +qZF26ULFdrZB4oWXAOliy/1S473OpQnp2MZp2asd0LPcg/BNaMuQrz44hxHb76R7 +qWD0ebIfEwKBgQCRCIiP1pjbVGN7ZOgPS080DSC+wClahtcyI+ZYLglTvRQTLDQ7 +LFtUykCav748MIADKuJBnM/3DiuCF5wV71EejDDfS/fo9BdyuKBY1brhixFTUX+E +Ww5Hc/SoLnpgALVZ/7jvWTpIBHykLxRziqYtR/YLzl+IkX/97P2ePoZ0rwKBgHNC +/7M5Z4JJyepfIMeVFHTCaT27TNTkf20x6Rs937U7TDN8y9JzEiU4LqXI4HAAhPoI +xnExRs4kF04YCnlRDE7Zs3Lv43J3ap1iTATfcymYwyv1RaQXEGQ/lUQHgYCZJtZz +fTrJoo5XyWu6nzJ5Gc8FLNaptr5ECSXGVm3Rsr2xAoGBAJWqEEQS/ejhO05QcPqh +y4cUdLr0269ILVsvic4Ot6zgfPIntXAK6IsHGKcg57kYm6W9k1CmmlA4ENGryJnR +vxyyqA9eyTFc1CQNuc2frKFA9It49JzjXahKc0aDHEHmTR787Tmk1LbuT0/gm9kA +L4INU6g+WqF0fatJxd+IJPrp +-----END PRIVATE KEY----- +`), + "ed25519": []byte(`-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACA+3f7hS7g5UWwXOGVTrMfhmxyrjqz7Sxxbx7I1j8DvvwAAAJhAFfkOQBX5 +DgAAAAtzc2gtZWQyNTUxOQAAACA+3f7hS7g5UWwXOGVTrMfhmxyrjqz7Sxxbx7I1j8Dvvw +AAAEAaYmXltfW6nhRo3iWGglRB48lYq0z0Q3I3KyrdutEr6j7d/uFLuDlRbBc4ZVOsx+Gb +HKuOrPtLHFvHsjWPwO+/AAAAE2dhcnRvbm1AZ2FydG9ubS14cHMBAg== +-----END OPENSSH PRIVATE KEY----- +`), + "rsa-openssh-format": []byte(`-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcn +NhAAAAAwEAAQAAAIEAwa48yfWFi3uIdqzuf9X7C2Zxfea/Iaaw0zIwHudpF8U92WVIiC5l +oEuW1+OaVi3UWfIEjWMV1tHGysrHOwtwc34BPCJqJknUQO/KtDTBTJ4Pryhw1bWPC999Lz +a+yrCTdNQYBzoROXKExZgPFh9pTMi5wqpHDuOQ2qZFIEI3lT0AAAIQWL0H31i9B98AAAAH +c3NoLXJzYQAAAIEAwa48yfWFi3uIdqzuf9X7C2Zxfea/Iaaw0zIwHudpF8U92WVIiC5loE +uW1+OaVi3UWfIEjWMV1tHGysrHOwtwc34BPCJqJknUQO/KtDTBTJ4Pryhw1bWPC999Lza+ +yrCTdNQYBzoROXKExZgPFh9pTMi5wqpHDuOQ2qZFIEI3lT0AAAADAQABAAAAgCThyTGsT4 +IARDxVMhWl6eiB2ZrgFgWSeJm/NOqtppWgOebsIqPMMg4UVuVFsl422/lE3RkPhVkjGXgE +pWvZAdCnmLmApK8wK12vF334lZhZT7t3Z9EzJps88PWEHo7kguf285HcnUM7FlFeissJdk +kXly34y7/3X/a6Tclm+iABAAAAQE0xR/KxZ39slwfMv64Rz7WKk1PPskaryI29aHE3mKHk +pY2QA+P3QlrKxT/VWUMjHUbNNdYfJm48xu0SGNMRdKMAAABBAORh2NP/06JUV3J9W/2Hju +X1ViJuqqcQnJPVzpgSL826EC2xwOECTqoY8uvFpUdD7CtpksIxNVqRIhuNOlz0lqEAAABB +ANkaHTTaPojClO0dKJ/Zjs7pWOCGliebBYprQ/Y4r9QLBkC/XaWMS26gFIrjgC7D2Rv+rZ +wSD0v0RcmkITP1ZR0AAAAYcHF1ZXJuYUBMdWNreUh5ZHJvLmxvY2FsAQID +-----END OPENSSH PRIVATE KEY-----`), + "p256-openssh-format": []byte(`-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSN5Ld/DFy8LJK0yrWg+Ryhq4/ifHry +QyCQeT4UXSB+UGdRct7kWA0hARbTaSCh+8U/Gs5O+IkDNoTKVsgxKUMQAAAAsO3C7nPtwu +5zAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBI3kt38MXLwskrTK +taD5HKGrj+J8evJDIJB5PhRdIH5QZ1Fy3uRYDSEBFtNpIKH7xT8azk74iQM2hMpWyDEpQx +AAAAAhAIHB48R+goZaiXndfYTrwk4BT1+MeLPC2/dwe0J5d1QDAAAAE21hcmlhbm9AZW5k +b3IubG9jYWwBAgME +-----END OPENSSH PRIVATE KEY-----`), + "p384-openssh-format": []byte(`-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNlY2RzYS +1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQTZb2VzEPs2NN/i1qHddKTVfwoIq3Tf +PeQ/kcWBvuCVJfIygvpm9MeusawEPuLSEXwiNDew+YHZ9xHIvFjCmZsLuEOzuh9t9KotwM +57H+7N+RDFzhM2j8hAaOuT5XDLKfUAAADgn/Sny5/0p8sAAAATZWNkc2Etc2hhMi1uaXN0 +cDM4NAAAAAhuaXN0cDM4NAAAAGEE2W9lcxD7NjTf4tah3XSk1X8KCKt03z3kP5HFgb7glS +XyMoL6ZvTHrrGsBD7i0hF8IjQ3sPmB2fcRyLxYwpmbC7hDs7ofbfSqLcDOex/uzfkQxc4T +No/IQGjrk+Vwyyn1AAAAMQDg0hwGKB/9Eq+e2FeTspi8QHW5xTD6prqsHDFx4cKk0ccgFV +61dhFhD/8SEbYlHzEAAAATbWFyaWFub0BlbmRvci5sb2NhbAECAwQ= +-----END OPENSSH PRIVATE KEY-----`), + "p521-openssh-format": []byte(`-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS +1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQBKzI3QSp1a2e1zMulZl1uFF1Y2Dnv +LSIwEu837hOV1epYEgNveAhGNm57TuBqYtnZeVfd2pzaz7CKX6N4B33N1XABQ5Ngji7lF2 +dUbmhNqJoMh43ioIsQNBaBenhmRpYP6f5k8P/7JZMIsLhkJk2hykb8maSZ+B3PYwPMNBdS +vP+0sHQAAAEYIsr2CCLK9ggAAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ +AAAIUEASsyN0EqdWtntczLpWZdbhRdWNg57y0iMBLvN+4TldXqWBIDb3gIRjZue07gamLZ +2XlX3dqc2s+wil+jeAd9zdVwAUOTYI4u5RdnVG5oTaiaDIeN4qCLEDQWgXp4ZkaWD+n+ZP +D/+yWTCLC4ZCZNocpG/Jmkmfgdz2MDzDQXUrz/tLB0AAAAQgEdeH+im6iRcP/juTAoeSHo +ExLtWhgL4JYqRwcOnzCKuLOPjEY/HSOuc+HRrbN9rbjsq+PcPHYe1NnkzXk0IW8hxQAAAB +NtYXJpYW5vQGVuZG9yLmxvY2FsAQIDBAUGBw== +-----END OPENSSH PRIVATE KEY-----`), + "user": []byte(`-----BEGIN EC PRIVATE KEY----- +MHcCAQEEILYCAeq8f7V4vSSypRw7pxy8yz3V5W4qg8kSC3zJhqpQoAoGCCqGSM49 +AwEHoUQDQgAEYcO2xNKiRUYOLEHM7VYAp57HNyKbOdYtHD83Z4hzNPVC4tM5mdGD +PLL8IEwvYu2wq+lpXfGQnNMbzYf9gspG0w== +-----END EC PRIVATE KEY----- +`), + "ca": []byte(`-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAvg9dQ9IRG59lYJb+GESfKWTch4yBpr7Ydw1jkK6vvtrx9jLo +5hkA8X6+ElRPRqTAZSlN5cBm6YCAcQIOsmXDUn6Oj1lVPQAoOjTBTvsjM3NjGhvv +52kHTY0nsMsBeY9q5DTtlzmlYkVUq2a6Htgf2mNi01dIw5fJ7uTTo8EbNf7O0i3u +c9a8P19HaZl5NKiWN4EIZkfB2WdXYRJCVBsGgQj3dE/GrEmH9QINq1A+GkNvK96u +vZm8H1jjmuqzHplWa7lFeXcx8FTVTbVb/iJrZ2Lc/JvIPitKZWhqbR59yrGjpwEp +Id7bo4WhO5L3OB0fSIJYvfu+o4WYnt4f3UzecwIDAQABAoIBABRD9yHgKErVuC2Q +bA+SYZY8VvdtF/X7q4EmQFORDNRA7EPgMc03JU6awRGbQ8i4kHs46EFzPoXvWcKz +AXYsO6N0Myc900Tp22A5d9NAHATEbPC/wdje7hRq1KyZONMJY9BphFv3nZbY5apR +Dc90JBFZP5RhXjTc3n9GjvqLAKfFEKVmPRCvqxCOZunw6XR+SgIQLJo36nsIsbhW +QUXIVaCI6cXMN8bRPm8EITdBNZu06Fpu4ZHm6VaxlXN9smERCDkgBSNXNWHKxmmA +c3Glo2DByUr2/JFBOrLEe9fkYgr24KNCQkHVcSaFxEcZvTggr7StjKISVHlCNEaB +7Q+kPoECgYEA3zE9FmvFGoQCU4g4Nl3dpQHs6kaAW8vJlrmq3xsireIuaJoa2HMe +wYdIvgCnK9DIjyxd5OWnE4jXtAEYPsyGD32B5rSLQrRO96lgb3f4bESCLUb3Bsn/ +sdgeE3p1xZMA0B59htqCrvVgN9k8WxyevBxYl3/gSBm/p8OVH1RTW/ECgYEA2f9Z +95OLj0KQHQtxQXf+I3VjhCw3LkLW39QZOXVI0QrCJfqqP7uxsJXH9NYX0l0GFTcR +kRrlyoaSU1EGQosZh+n1MvplGBTkTSV47/bPsTzFpgK2NfEZuFm9RoWgltS+nYeH +Y2k4mnAN3PhReCMwuprmJz8GRLsO3Cs2s2YylKMCgYEA2UX+uO/q7jgqZ5UJW+ue +1H5+W0aMuFA3i7JtZEnvRaUVFqFGlwXin/WJ2+WY1++k/rPrJ+Rk9IBXtBUIvEGw +FC5TIfsKQsJyyWgqx/jbbtJ2g4s8+W/1qfTAuqeRNOg5d2DnRDs90wJuS4//0JaY +9HkHyVwkQyxFxhSA/AHEMJECgYA2MvyFR1O9bIk0D3I7GsA+xKLXa77Ua53MzIjw +9i4CezBGDQpjCiFli/fI8am+jY5DnAtsDknvjoG24UAzLy5L0mk6IXMdB6SzYYut +7ak5oahqW+Y9hxIj+XvLmtGQbphtxhJtLu35x75KoBpxSh6FZpmuTEccs31AVCYn +eFM/DQKBgQDOPUwbLKqVi6ddFGgrV9MrWw+SWsDa43bPuyvYppMM3oqesvyaX1Dt +qDvN7owaNxNM4OnfKcZr91z8YPVCFo4RbBif3DXRzjNNBlxEjHBtuMOikwvsmucN +vIrbeEpjTiUMTEAr6PoTiVHjsfS8WAM6MDlF5M+2PNswDsBpa2yLgA== +-----END RSA PRIVATE KEY----- +`), +} + +var PEMEncryptedKeys = []struct { + Name string + EncryptionKey string + IncludesPublicKey bool + PEMBytes []byte +}{ + 0: { + Name: "rsa-encrypted", + EncryptionKey: "r54-G0pher_t3st$", + PEMBytes: []byte(`-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,3E1714DE130BC5E81327F36564B05462 + +MqW88sud4fnWk/Jk3fkjh7ydu51ZkHLN5qlQgA4SkAXORPPMj2XvqZOv1v2LOgUV +dUevUn8PZK7a9zbZg4QShUSzwE5k6wdB7XKPyBgI39mJ79GBd2U4W3h6KT6jIdWA +goQpluxkrzr2/X602IaxLEre97FT9mpKC6zxKCLvyFWVIP9n3OSFS47cTTXyFr+l +7PdRhe60nn6jSBgUNk/Q1lAvEQ9fufdPwDYY93F1wyJ6lOr0F1+mzRrMbH67NyKs +rG8J1Fa7cIIre7ueKIAXTIne7OAWqpU9UDgQatDtZTbvA7ciqGsSFgiwwW13N+Rr +hN8MkODKs9cjtONxSKi05s206A3NDU6STtZ3KuPDjFE1gMJODotOuqSM+cxKfyFq +wxpk/CHYCDdMAVBSwxb/vraOHamylL4uCHpJdBHypzf2HABt+lS8Su23uAmL87DR +yvyCS/lmpuNTndef6qHPRkoW2EV3xqD3ovosGf7kgwGJUk2ZpCLVteqmYehKlZDK +r/Jy+J26ooI2jIg9bjvD1PZq+Mv+2dQ1RlDrPG3PB+rEixw6vBaL9x3jatCd4ej7 +XG7lb3qO9xFpLsx89tkEcvpGR+broSpUJ6Mu5LBCVmrvqHjvnDhrZVz1brMiQtU9 +iMZbgXqDLXHd6ERWygk7OTU03u+l1gs+KGMfmS0h0ZYw6KGVLgMnsoxqd6cFSKNB +8Ohk9ZTZGCiovlXBUepyu8wKat1k8YlHSfIHoRUJRhhcd7DrmojC+bcbMIZBU22T +Pl2ftVRGtcQY23lYd0NNKfebF7ncjuLWQGy+vZW+7cgfI6wPIbfYfP6g7QAutk6W +KQx0AoX5woZ6cNxtpIrymaVjSMRRBkKQrJKmRp3pC/lul5E5P2cueMs1fj4OHTbJ +lAUv88ywr+R+mRgYQlFW/XQ653f6DT4t6+njfO9oBcPrQDASZel3LjXLpjjYG/N5 ++BWnVexuJX9ika8HJiFl55oqaKb+WknfNhk5cPY+x7SDV9ywQeMiDZpr0ffeYAEP +LlwwiWRDYpO+uwXHSFF3+JjWwjhs8m8g99iFb7U93yKgBB12dCEPPa2ZeH9wUHMJ +sreYhNuq6f4iWWSXpzN45inQqtTi8jrJhuNLTT543ErW7DtntBO2rWMhff3aiXbn +Uy3qzZM1nPbuCGuBmP9L2dJ3Z5ifDWB4JmOyWY4swTZGt9AVmUxMIKdZpRONx8vz +I9u9nbVPGZBcou50Pa0qTLbkWsSL94MNXrARBxzhHC9Zs6XNEtwN7mOuii7uMkVc +adrxgknBH1J1N+NX/eTKzUwJuPvDtA+Z5ILWNN9wpZT/7ed8zEnKHPNUexyeT5g3 +uw9z9jH7ffGxFYlx87oiVPHGOrCXYZYW5uoZE31SCBkbtNuffNRJRKIFeipmpJ3P +7bpAG+kGHMelQH6b+5K1Qgsv4tpuSyKeTKpPFH9Av5nN4P1ZBm9N80tzbNWqjSJm +S7rYdHnuNEVnUGnRmEUMmVuYZnNBEVN/fP2m2SEwXcP3Uh7TiYlcWw10ygaGmOr7 +MvMLGkYgQ4Utwnd98mtqa0jr0hK2TcOSFir3AqVvXN3XJj4cVULkrXe4Im1laWgp +-----END RSA PRIVATE KEY----- +`), + }, + + 1: { + Name: "dsa-encrypted", + EncryptionKey: "qG0pher-dsa_t3st$", + PEMBytes: []byte(`-----BEGIN DSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,7CE7A6E4A647DC01AF860210B15ADE3E + +hvnBpI99Hceq/55pYRdOzBLntIEis02JFNXuLEydWL+RJBFDn7tA+vXec0ERJd6J +G8JXlSOAhmC2H4uK3q2xR8/Y3yL95n6OIcjvCBiLsV+o3jj1MYJmErxP6zRtq4w3 +JjIjGHWmaYFSxPKQ6e8fs74HEqaeMV9ONUoTtB+aISmgaBL15Fcoayg245dkBvVl +h5Kqspe7yvOBmzA3zjRuxmSCqKJmasXM7mqs3vIrMxZE3XPo1/fWKcPuExgpVQoT +HkJZEoIEIIPnPMwT2uYbFJSGgPJVMDT84xz7yvjCdhLmqrsXgs5Qw7Pw0i0c0BUJ +b7fDJ2UhdiwSckWGmIhTLlJZzr8K+JpjCDlP+REYBI5meB7kosBnlvCEHdw2EJkH +0QDc/2F4xlVrHOLbPRFyu1Oi2Gvbeoo9EsM/DThpd1hKAlb0sF5Y0y0d+owv0PnE +R/4X3HWfIdOHsDUvJ8xVWZ4BZk9Zk9qol045DcFCehpr/3hslCrKSZHakLt9GI58 +vVQJ4L0aYp5nloLfzhViZtKJXRLkySMKdzYkIlNmW1oVGl7tce5UCNI8Nok4j6yn +IiHM7GBn+0nJoKTXsOGMIBe3ulKlKVxLjEuk9yivh/8= +-----END DSA PRIVATE KEY----- +`), + }, + + 2: { + Name: "ed25519-encrypted", + EncryptionKey: "password", + IncludesPublicKey: true, + PEMBytes: []byte(`-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDKj29BlC +ocEWuVhQ94/RjoAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIIw1gSurPTDwZidA +2AIjQZgoQi3IFn9jBtFdP10/Jj7DAAAAoFGkQbB2teSU7ikUsnc7ct2aH3pitM359lNVUh +7DQbJWMjbQFbrBYyDJP+ALj1/RZmP2yoIf7/wr99q53/pm28Xp1gGP5V2RGRJYCA6kgFIH +xdB6KEw1Ce7Bz8JaDIeagAGd3xtQTH3cuuleVxCZZnk9NspsPxigADKCls/RUiK7F+z3Qf +Lvs9+PH8nIuhFMYZgo3liqZbVS5z4Fqhyzyq4= +-----END OPENSSH PRIVATE KEY----- +`), + }, + + 3: { + Name: "ed25519-encrypted-cbc", + EncryptionKey: "password", + IncludesPublicKey: true, + PEMBytes: []byte(`-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABDzGKF3uX +G1gXALZKFd6Ir4AAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIDne4/teO42zTDdj +NwxUMNpbfmp/dxgU4ZNkC3ydgcugAAAAoJ3J/oA7+iqVOz0CIUUk9ufdP1VP4jDf2um+0s +Sgs7x6Gpyjq67Ps7wLRdSmxr/G5b+Z8dRGFYS/wUCQEe3whwuImvLyPwWjXLzkAyMzc01f +ywBGSrHnvP82ppenc2HuTI+E05Xc02i6JVyI1ShiekQL5twoqtR6pEBZnD17UonIx7cRzZ +gbDGyT3bXMQtagvCwoW+/oMTKXiZP5jCJpEO8= +-----END OPENSSH PRIVATE KEY----- +`), + }, +} diff --git a/lib/service/connect.go b/lib/service/connect.go index 4eea0f1e2a631..c6c6e727892ff 100644 --- a/lib/service/connect.go +++ b/lib/service/connect.go @@ -189,7 +189,7 @@ func (process *TeleportProcess) connectToAuthService(role types.SystemRole, opts if err != nil { return nil, trace.Wrap(err) } - process.logger.DebugContext(process.ExitContext(), "Client successfully connected to cluster", "client_identity", connector.ClientIdentityString()) + process.logger.DebugContext(process.ExitContext(), "Client successfully connected to cluster", "client_identity", connector.clientIdentityString()) process.addConnector(connector) return connector, nil @@ -230,10 +230,7 @@ func (process *TeleportProcess) connect(role types.SystemRole, opts ...certOptio // The roles of admin and auth are treated in a special way, as in this case // the process does not need TLS clients and can use local auth directly. if role == types.RoleAdmin || role == types.RoleAuth { - return &Connector{ - clientIdentity: identity, - serverIdentity: identity, - }, nil + return newConnector(identity, identity) } process.logger.InfoContext(process.ExitContext(), "Connecting to the cluster with TLS client certificate.", "cluster", identity.ClusterName) connector, err := process.getConnector(identity, identity) @@ -256,10 +253,7 @@ func (process *TeleportProcess) connect(role types.SystemRole, opts ...certOptio // Both clients and servers are using old credentials, // this phase exists for remote clusters to propagate information about the new CA if role == types.RoleAdmin || role == types.RoleAuth { - return &Connector{ - clientIdentity: identity, - serverIdentity: identity, - }, nil + return newConnector(identity, identity) } connector, err := process.getConnector(identity, identity) return connector, trace.Wrap(err) @@ -271,10 +265,7 @@ func (process *TeleportProcess) connect(role types.SystemRole, opts ...certOptio return nil, trace.Wrap(err) } if role == types.RoleAdmin || role == types.RoleAuth { - return &Connector{ - clientIdentity: newIdentity, - serverIdentity: identity, - }, nil + return newConnector(newIdentity, identity) } connector, err := process.getConnector(newIdentity, identity) return connector, trace.Wrap(err) @@ -286,10 +277,7 @@ func (process *TeleportProcess) connect(role types.SystemRole, opts ...certOptio return nil, trace.Wrap(err) } if role == types.RoleAdmin || role == types.RoleAuth { - return &Connector{ - clientIdentity: newIdentity, - serverIdentity: newIdentity, - }, nil + return newConnector(newIdentity, newIdentity) } connector, err := process.getConnector(newIdentity, newIdentity) return connector, trace.Wrap(err) @@ -299,10 +287,7 @@ func (process *TeleportProcess) connect(role types.SystemRole, opts ...certOptio // but the new certificate authority should be trusted // because not all clients can update at the same time. if role == types.RoleAdmin || role == types.RoleAuth { - return &Connector{ - clientIdentity: identity, - serverIdentity: identity, - }, nil + return newConnector(identity, identity) } connector, err := process.getConnector(identity, identity) return connector, trace.Wrap(err) @@ -388,7 +373,7 @@ func (process *TeleportProcess) getCertAuthority(conn *Connector, id types.CertA // In case if auth servers, the role is 'TeleportAdmin' and instead of using // TLS client this method uses the local auth server. func (process *TeleportProcess) reRegister(conn *Connector, additionalPrincipals []string, dnsNames []string, rotation types.Rotation, systemRoles []types.SystemRole, assertionID string) (*state.Identity, error) { - id := conn.clientIdentity.ID + id := conn.clientState.Load().identity.ID if id.NodeName == "" { id.NodeName = process.Config.Hostname } @@ -526,9 +511,9 @@ func (process *TeleportProcess) firstTimeConnect(role types.SystemRole) (*Connec process.logger.InfoContext(process.ExitContext(), "Successfully obtained credentials to connect to the cluster.", "identity", role) var connector *Connector if role == types.RoleAdmin || role == types.RoleAuth { - connector = &Connector{ - clientIdentity: identity, - serverIdentity: identity, + connector, err = newConnector(identity, identity) + if err != nil { + return nil, trace.Wrap(err) } } else { connector, err = process.getConnector(identity, identity) @@ -900,13 +885,14 @@ func checkServerIdentity(ctx context.Context, conn *Connector, additionalPrincip // If advertise_ip, public_addr, or listen_addr in file configuration were // updated, the list of principals (SSH) or DNS names (TLS) on the // certificate need to be updated. - if len(additionalPrincipals) != 0 && !conn.serverIdentity.HasPrincipals(principalsToCheck) { + serverIdentity := conn.serverState.Load().identity + if len(additionalPrincipals) != 0 && !serverIdentity.HasPrincipals(principalsToCheck) { principalsChanged = true - logger.InfoContext(ctx, "Rotation in progress, updating SSH principals.", "identity", conn.serverIdentity.ID.Role, "additional_principals", additionalPrincipals, "current_principals", conn.serverIdentity.Cert.ValidPrincipals) + logger.InfoContext(ctx, "Rotation in progress, updating SSH principals.", "identity", serverIdentity.ID.Role, "additional_principals", additionalPrincipals, "current_principals", serverIdentity.Cert.ValidPrincipals) } - if len(dnsNames) != 0 && !conn.serverIdentity.HasDNSNames(dnsNames) { + if len(dnsNames) != 0 && !serverIdentity.HasDNSNames(dnsNames) { dnsNamesChanged = true - logger.InfoContext(ctx, "Rotation in progress, updating DNS names.", "identity", conn.serverIdentity.ID.Role, "additional_dns_names", dnsNames, "current_dns_names", conn.serverIdentity.XCert.DNSNames) + logger.InfoContext(ctx, "Rotation in progress, updating DNS names.", "identity", serverIdentity.ID.Role, "additional_dns_names", dnsNames, "current_dns_names", serverIdentity.XCert.DNSNames) } return principalsChanged || dnsNamesChanged @@ -914,7 +900,8 @@ func checkServerIdentity(ctx context.Context, conn *Connector, additionalPrincip // rotate is called to check if rotation should be triggered. func (process *TeleportProcess) rotate(conn *Connector, localState state.StateV2, remote types.Rotation) (*rotationStatus, error) { - id := conn.clientIdentity.ID + clientIdentity := conn.clientState.Load().identity + id := clientIdentity.ID local := localState.Spec.Rotation additionalPrincipals, dnsNames, err := process.getAdditionalPrincipals(id.Role) @@ -927,7 +914,7 @@ func (process *TeleportProcess) rotate(conn *Connector, localState state.StateV2 var wantsSystemRoleRepair bool if id.Role == types.RoleInstance { var baseSystemRoles []types.SystemRole - for _, baseRole := range conn.clientIdentity.SystemRoles { + for _, baseRole := range clientIdentity.SystemRoles { baseSystemRoles = append(baseSystemRoles, types.SystemRole(baseRole)) } var danglingSystemRoles []types.SystemRole @@ -939,7 +926,7 @@ func (process *TeleportProcess) rotate(conn *Connector, localState state.StateV2 } if len(danglingSystemRoles) != 0 { - process.logger.WarnContext(process.ExitContext(), "rotation logic detected dangling system role(s), will attempt to self-repair", "existing", conn.clientIdentity.SystemRoles, "dangling", danglingSystemRoles) + process.logger.WarnContext(process.ExitContext(), "rotation logic detected dangling system role(s), will attempt to self-repair", "existing", clientIdentity.SystemRoles, "dangling", danglingSystemRoles) // if auth is running locally, we can just generate ourselves a new instance cert with the correct // roles. otherwise, we need to prove to the remote auth server that we hold the necessary privileges // by performing system role assertions. @@ -1105,20 +1092,22 @@ func (process *TeleportProcess) rotate(conn *Connector, localState state.StateV2 // getConnector gets an appropriate [Connector] for the given identity. The returned [Connector] is backed by the // instance client is reused if appropriate, otherwise a new client is created. func (process *TeleportProcess) getConnector(clientIdentity, serverIdentity *state.Identity) (*Connector, error) { + newConn, err := newConnector(clientIdentity, serverIdentity) + if err != nil { + return nil, trace.Wrap(err) + } if clientIdentity.ID.Role != types.RoleInstance { // non-instance roles should wait to see if the instance client can be reused // before acquiring their own client. - if conn := process.waitForInstanceConnector(); conn != nil && conn.Client != nil { - if conn.clientIdentity.HasSystemRole(clientIdentity.ID.Role) { - process.logger.InfoContext(process.ExitContext(), "Reusing Instance client.", "identity", clientIdentity.ID.Role, "additional_system_roles", conn.clientIdentity.SystemRoles) - return &Connector{ - clientIdentity: clientIdentity, - serverIdentity: serverIdentity, - Client: conn.Client, - ReusedClient: true, - }, nil + if instanceConn := process.waitForInstanceConnector(); instanceConn != nil && instanceConn.Client != nil { + instanceClientIdentity := instanceConn.clientState.Load().identity + if instanceClientIdentity.HasSystemRole(clientIdentity.ID.Role) { + process.logger.InfoContext(process.ExitContext(), "Reusing Instance client.", "identity", clientIdentity.ID.Role, "additional_system_roles", instanceClientIdentity.SystemRoles) + newConn.Client = instanceConn.Client + newConn.ReusedClient = true + return newConn, nil } else { - process.logger.WarnContext(process.ExitContext(), "Unable to reuse Instance client.", "identity", clientIdentity.ID.Role, "additional_system_roles", conn.clientIdentity.SystemRoles) + process.logger.WarnContext(process.ExitContext(), "Unable to reuse Instance client.", "identity", clientIdentity.ID.Role, "additional_system_roles", instanceClientIdentity.SystemRoles) } } else { process.logger.WarnContext(process.ExitContext(), "Instance client not available for reuse.", "identity", clientIdentity.ID.Role) @@ -1141,11 +1130,8 @@ func (process *TeleportProcess) getConnector(clientIdentity, serverIdentity *sta process.setAuthSubjectiveAddr(pingResponse.RemoteAddr) process.logger.InfoContext(process.ExitContext(), "features loaded from auth server", "identity", clientIdentity.ID.Role, "features", pingResponse.GetServerFeatures()) - return &Connector{ - clientIdentity: clientIdentity, - serverIdentity: serverIdentity, - Client: clt, - }, nil + newConn.Client = clt + return newConn, nil } // newClient attempts to connect to either the proxy server or auth server diff --git a/lib/service/discovery.go b/lib/service/discovery.go index 79e4eedb87dc7..4c6105f246234 100644 --- a/lib/service/discovery.go +++ b/lib/service/discovery.go @@ -63,17 +63,6 @@ func (process *TeleportProcess) initDiscoveryService() error { if err != nil { return trace.Wrap(err) } - // tlsConfig is the DiscoveryService's TLS certificate signed by the cluster's - // Host certificate authority. - // It is used to authenticate the DiscoveryService to the Access Graph service. - tlsConfig, err := conn.ServerTLSConfig(process.Config.CipherSuites) - if err != nil { - return trace.Wrap(err) - } - - if tlsConfig != nil { - tlsConfig.ServerName = "" /* empty the server name to avoid SNI collisions with access graph addr */ - } accessGraphCfg, err := buildAccessGraphFromTAGOrFallbackToAuth( process.ExitContext(), @@ -102,7 +91,7 @@ func (process *TeleportProcess) initDiscoveryService() error { ClusterName: conn.ClusterName(), ClusterFeatures: process.GetClusterFeatures, PollInterval: process.Config.Discovery.PollInterval, - ServerCredentials: tlsConfig, + GetClientCert: conn.ClientGetCertificate, AccessGraphConfig: accessGraphCfg, }) if err != nil { diff --git a/lib/service/service.go b/lib/service/service.go index 64cc5d059d7ae..0ed957687395b 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -84,6 +84,7 @@ import ( "github.com/gravitational/teleport/api/utils/aws" "github.com/gravitational/teleport/api/utils/grpc/interceptors" "github.com/gravitational/teleport/api/utils/keys" + apisshutils "github.com/gravitational/teleport/api/utils/sshutils" "github.com/gravitational/teleport/lib" "github.com/gravitational/teleport/lib/agentless" "github.com/gravitational/teleport/lib/auditd" @@ -158,6 +159,7 @@ import ( "github.com/gravitational/teleport/lib/srv/transport/transportv1" "github.com/gravitational/teleport/lib/sshutils" "github.com/gravitational/teleport/lib/system" + "github.com/gravitational/teleport/lib/tlsca" usagereporter "github.com/gravitational/teleport/lib/usagereporter/teleport" "github.com/gravitational/teleport/lib/utils" awsutils "github.com/gravitational/teleport/lib/utils/aws" @@ -296,18 +298,97 @@ const ( TeleportOKEvent = "TeleportOKEvent" ) +func newConnector(clientIdentity, serverIdentity *state.Identity) (*Connector, error) { + clientState, err := newConnectorState(clientIdentity) + if err != nil { + return nil, trace.Wrap(err) + } + serverState, err := newConnectorState(serverIdentity) + if err != nil { + return nil, trace.Wrap(err) + } + c := &Connector{ + clusterName: clientIdentity.ClusterName, + hostID: clientIdentity.ID.HostUUID, + role: clientIdentity.ID.Role, + } + c.clientState.Store(clientState) + c.serverState.Store(serverState) + return c, nil +} + +func newConnectorState(identity *state.Identity) (*connectorState, error) { + state := &connectorState{ + identity: identity, + } + if identity.Cert != nil { + hostCheckers, err := apisshutils.ParseAuthorizedKeys(identity.SSHCACertBytes) + if err != nil { + return nil, trace.Wrap(err, "parsing SSH host CAs") + } + state.hostCheckers = hostCheckers + + state.sshCert = identity.Cert + state.sshCertSigner = identity.KeySigner + } + if identity.HasTLSConfig() { + tlsCert, err := keys.X509KeyPair(identity.TLSCertBytes, identity.KeyBytes) + if err != nil { + return nil, trace.Wrap(err, "parsing X.509 certificate") + } + tlsCert.Leaf = identity.XCert + certPool := x509.NewCertPool() + for j := range identity.TLSCACertsBytes { + parsedCert, err := tlsca.ParseCertificatePEM(identity.TLSCACertsBytes[j]) + if err != nil { + return nil, trace.Wrap(err, "parsing X.509 host CA") + } + certPool.AddCert(parsedCert) + } + state.tlsCert = &tlsCert + state.pool = certPool + } + return state, nil +} + +// connectorState contains immutable state (generally derived from a +// [*state.Identity]) suitable for sharing behind an atomic pointer. +type connectorState struct { + identity *state.Identity + + // tlsCert is the TLS client certificate for the identity, with Signer and + // Leaf filled. + tlsCert *tls.Certificate + // pool contains the host CA certificates trusted by the identity. + pool *x509.CertPool + + // sshCert is the SSH certificate associated with the identity. + sshCert *ssh.Certificate + // sshCertSigner is a [ssh.Signer] presenting the sshCert certificate as its + // public key. + sshCertSigner ssh.Signer + // hostCheckers contains the (non-certificate) public keys that make up the + // host CA trusted by the identity. + hostCheckers []ssh.PublicKey +} + // Connector has all resources process needs to connect to other parts of the // cluster: client and identity. type Connector struct { - // clientIdentity is the identity to be used in internal cluster - // clients to the auth service. - clientIdentity *state.Identity - - // serverIdentity is the identity to be used in servers - serving SSH - // and x509 certificates to clients. - serverIdentity *state.Identity - - // Client is authenticated client with credentials from ClientIdentity. + clusterName string + hostID string + role types.SystemRole + + // clientState contains the current connector state for outbound connections + // to the cluster. + clientState atomic.Pointer[connectorState] + // serverState contains the current connector state for inbound connections + // from the cluster. + serverState atomic.Pointer[connectorState] + + // Client is an authenticated client intended to use the credentials in + // clientState (unless it's a client shared from some other connector as + // signified by ReusedClient). Client *authclient.Client // ReusedClient, if true, indicates that the client reference is owned by @@ -316,65 +397,105 @@ type Connector struct { } func (c *Connector) ClusterName() string { - return c.clientIdentity.ClusterName + return c.clusterName } func (c *Connector) HostID() string { - return c.clientIdentity.ID.HostUUID + return c.hostID } func (c *Connector) Role() types.SystemRole { - return c.clientIdentity.ID.Role -} - -func (c *Connector) ClientTLSConfig(cipherSuites []uint16) (*tls.Config, error) { - return c.clientIdentity.TLSConfig(cipherSuites) + return c.role } +// ClientGetCertificate returns the current credentials for outgoing TLS +// connections to other cluster components. func (c *Connector) ClientGetCertificate() (*tls.Certificate, error) { - cert, err := keys.X509KeyPair(c.clientIdentity.TLSCertBytes, c.clientIdentity.KeyBytes) - if err != nil { - return nil, trace.Wrap(err) + tlsCert := c.clientState.Load().tlsCert + if tlsCert == nil { + return nil, trace.NotFound("no TLS credentials setup for this identity") } - return &cert, nil + return tlsCert, nil } -func (c *Connector) ClientAuthMethods() []ssh.AuthMethod { - return []ssh.AuthMethod{ssh.PublicKeys(c.clientIdentity.KeySigner)} +// ClientGetPool returns a pool with the trusted X.509/TLS signers from the host +// CA of the local cluster, as known by the connector. +func (c *Connector) ClientGetPool() (*x509.CertPool, error) { + roots := c.clientState.Load().pool + if roots == nil { + return nil, trace.NotFound("no TLS credentials setup for this identity") + } + return roots, nil } -func (c *Connector) ClientIdentityString() string { - return c.clientIdentity.String() +// ClientAuthMethods returns the [ssh.AuthMethod]s that should be used for +// outgoing SSH connections to other cluster components (the Proxy Service, +// almost surely). +func (c *Connector) ClientAuthMethods() []ssh.AuthMethod { + return []ssh.AuthMethod{ + ssh.PublicKeysCallback(func() (signers []ssh.Signer, err error) { + sshCertSigner := c.clientState.Load().sshCertSigner + if sshCertSigner == nil { + return nil, nil + } + return []ssh.Signer{sshCertSigner}, nil + }), + } } -func (c *Connector) ClientInstanceSystemRoles() []string { - return slices.Clone(c.clientIdentity.SystemRoles) +func (c *Connector) clientIdentityString() string { + return c.clientState.Load().identity.String() } +// ServerTLSConfig returns a new server-side [*tls.Config] that presents the +// connector's credentials as its certificate. The returned tls.Config doesn't +// request or trust any client certificates, so the caller is responsible for +// configuring it. func (c *Connector) ServerTLSConfig(cipherSuites []uint16) (*tls.Config, error) { - return c.serverIdentity.TLSConfig(cipherSuites) + conf := utils.TLSConfig(cipherSuites) + conf.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) { + tlsCert := c.serverState.Load().tlsCert + if tlsCert == nil { + return nil, trace.NotFound("no TLS credentials setup for this identity") + } + return tlsCert, nil + } + return conf, nil } +// ServerGetHostSigners returns the [ssh.Signer]s that should be used as host +// keys for incoming SSH connections. func (c *Connector) ServerGetHostSigners() []ssh.Signer { - return []ssh.Signer{c.serverIdentity.KeySigner} + sshCertSigner := c.serverState.Load().sshCertSigner + if sshCertSigner == nil { + return nil + } + return []ssh.Signer{sshCertSigner} } func (c *Connector) ServerGetValidPrincipals() []string { - return slices.Clone(c.serverIdentity.Cert.ValidPrincipals) + // TODO(espadolini): get rid of this function after refactoring the two + // integration tests that use it + sshCert := c.serverState.Load().sshCert + if sshCert == nil { + return nil + } + return slices.Clone(sshCert.ValidPrincipals) } func (c *Connector) getPROXYSigner(clock clockwork.Clock) (multiplexer.PROXYHeaderSigner, error) { - signer, err := keys.ParsePrivateKey(c.serverIdentity.KeyBytes) + serverIdentity := c.serverState.Load().identity + signer, err := keys.ParsePrivateKey(serverIdentity.KeyBytes) if err != nil { return nil, trace.Wrap(err, "could not parse identity's private key") } - jwtSigner, err := services.GetJWTSigner(signer, c.serverIdentity.ClusterName, clock) + jwtSigner, err := services.GetJWTSigner(signer, serverIdentity.ClusterName, clock) if err != nil { return nil, trace.Wrap(err, "could not create JWT signer") } - proxySigner, err := multiplexer.NewPROXYSigner(c.serverIdentity.XCert, jwtSigner) + proxySigner, err := multiplexer.NewPROXYSigner(serverIdentity.XCert, jwtSigner) if err != nil { return nil, trace.Wrap(err, "could not create PROXY signer") } @@ -2956,7 +3077,7 @@ func (process *TeleportProcess) initSSH() error { process.ExitContext(), reversetunnel.AgentPoolConfig{ Component: teleport.ComponentNode, - HostUUID: conn.serverIdentity.ID.HostUUID, + HostUUID: conn.HostID(), Resolver: conn.TunnelProxyResolver(), Client: conn.Client, AccessPoint: authClient, @@ -4089,11 +4210,6 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { return trace.Wrap(err) } - clientTLSConfig, err := conn.ClientTLSConfig(cfg.CipherSuites) - if err != nil { - return trace.Wrap(err) - } - clusterNetworkConfig, err := accessPoint.GetClusterNetworkingConfig(process.ExitContext()) if err != nil { return trace.Wrap(err) @@ -4192,14 +4308,16 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { if !process.Config.Proxy.DisableReverseTunnel { if listeners.proxyPeer != nil { peerClient, err = peer.NewClient(peer.ClientConfig{ - Context: process.ExitContext(), - ID: process.Config.HostUUID, - AuthClient: conn.Client, - AccessPoint: accessPoint, - TLSConfig: clientTLSConfig, - Log: process.log, - Clock: process.Clock, - ClusterName: clusterName, + Context: process.ExitContext(), + ID: process.Config.HostUUID, + AuthClient: conn.Client, + AccessPoint: accessPoint, + TLSCipherSuites: cfg.CipherSuites, + GetTLSCertificate: conn.ClientGetCertificate, + GetTLSRoots: conn.ClientGetPool, + Log: process.log, + Clock: process.Clock, + ClusterName: clusterName, }) if err != nil { return trace.Wrap(err) @@ -4213,11 +4331,13 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { tsrv, err = reversetunnel.NewServer( reversetunnel.Config{ + ClientTLSCipherSuites: process.Config.CipherSuites, + GetClientTLSCertificate: conn.ClientGetCertificate, + Context: process.ExitContext(), Component: teleport.Component(teleport.ComponentProxy, process.id), ID: process.Config.HostUUID, ClusterName: clusterName, - ClientTLS: clientTLSConfig, Listener: rtListener, GetHostSigners: conn.ServerGetHostSigners, LocalAuthClient: conn.Client, @@ -4433,30 +4553,30 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { }) webConfig := web.Config{ - Proxy: tsrv, - AuthServers: cfg.AuthServerAddresses()[0], - DomainName: cfg.Hostname, - ProxyClient: conn.Client, - ProxySSHAddr: proxySSHAddr, - ProxyWebAddr: cfg.Proxy.WebAddr, - ProxyPublicAddrs: cfg.Proxy.PublicAddrs, - CipherSuites: cfg.CipherSuites, - FIPS: cfg.FIPS, - AccessPoint: accessPoint, - Emitter: asyncEmitter, - PluginRegistry: process.PluginRegistry, - HostUUID: process.Config.HostUUID, - Context: process.GracefulExitContext(), - StaticFS: fs, - ClusterFeatures: process.GetClusterFeatures(), - GetProxyClientTLSConfig: conn.ClientTLSConfig, - UI: cfg.Proxy.UI, - ProxySettings: proxySettings, - PublicProxyAddr: process.proxyPublicAddr().Addr, - ALPNHandler: alpnHandlerForWeb.HandleConnection, - ProxyKubeAddr: proxyKubeAddr, - TraceClient: traceClt, - Router: proxyRouter, + Proxy: tsrv, + AuthServers: cfg.AuthServerAddresses()[0], + DomainName: cfg.Hostname, + ProxyClient: conn.Client, + ProxySSHAddr: proxySSHAddr, + ProxyWebAddr: cfg.Proxy.WebAddr, + ProxyPublicAddrs: cfg.Proxy.PublicAddrs, + CipherSuites: cfg.CipherSuites, + FIPS: cfg.FIPS, + AccessPoint: accessPoint, + Emitter: asyncEmitter, + PluginRegistry: process.PluginRegistry, + HostUUID: process.Config.HostUUID, + Context: process.GracefulExitContext(), + StaticFS: fs, + ClusterFeatures: process.GetClusterFeatures(), + GetProxyClientCertificate: conn.ClientGetCertificate, + UI: cfg.Proxy.UI, + ProxySettings: proxySettings, + PublicProxyAddr: process.proxyPublicAddr().Addr, + ALPNHandler: alpnHandlerForWeb.HandleConnection, + ProxyKubeAddr: proxyKubeAddr, + TraceClient: traceClt, + Router: proxyRouter, SessionControl: web.SessionControllerFunc(func(ctx context.Context, sctx *web.SessionContext, login, localAddr, remoteAddr string) (context.Context, error) { controller := srv.WebSessionController(sessionController) ctx, err := controller(ctx, sctx, login, localAddr, remoteAddr) @@ -4555,10 +4675,23 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { return trace.Wrap(err) } peerAddrString = peerAddr.String() + + // TODO(espadolini): once connectors are live updated we can get rid of + // this and just refer to the host CA pool in the connector instead + peerServerTLSConfig := serverTLSConfig.Clone() + peerServerTLSConfig.GetConfigForClient = func(chi *tls.ClientHelloInfo) (*tls.Config, error) { + pool, _, err := authclient.ClientCertPool(chi.Context(), accessPoint, clusterName, types.HostCA) + if err != nil { + return nil, trace.Wrap(err) + } + tlsConfig := peerServerTLSConfig.Clone() + tlsConfig.ClientCAs = pool + return tlsConfig, nil + } + proxyServer, err = peer.NewServer(peer.ServerConfig{ - AccessCache: accessPoint, Listener: listeners.proxyPeer, - TLSConfig: serverTLSConfig, + TLSConfig: peerServerTLSConfig, ClusterDialer: clusterdial.NewClusterDialer(tsrv), Log: process.log.WithField(teleport.ComponentKey, teleport.Component(teleport.ComponentReverseTunnelServer, process.id)), ClusterName: clusterName, @@ -4648,16 +4781,16 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { ClusterName: clusterName, } - tlscfg := serverTLSConfig.Clone() - tlscfg.ClientAuth = tls.RequireAndVerifyClientCert + sshGRPCTLSConfig := serverTLSConfig.Clone() + sshGRPCTLSConfig.ClientAuth = tls.RequireAndVerifyClientCert if lib.IsInsecureDevMode() { - tlscfg.InsecureSkipVerify = true - tlscfg.ClientAuth = tls.RequireAnyClientCert + sshGRPCTLSConfig.InsecureSkipVerify = true + sshGRPCTLSConfig.ClientAuth = tls.RequireAnyClientCert } // clientTLSConfigGenerator pre-generates specialized per-cluster client TLS config values clientTLSConfigGenerator, err := auth.NewClientTLSConfigGenerator(auth.ClientTLSConfigGeneratorConfig{ - TLS: tlscfg.Clone(), + TLS: sshGRPCTLSConfig, ClusterName: clusterName, PermitRemoteClusters: true, AccessPoint: accessPoint, @@ -4665,11 +4798,10 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { if err != nil { return trace.Wrap(err) } + sshGRPCTLSConfig.GetConfigForClient = clientTLSConfigGenerator.GetConfigForClient - tlscfg.GetConfigForClient = clientTLSConfigGenerator.GetConfigForClient - - creds, err := auth.NewTransportCredentials(auth.TransportCredentialsConfig{ - TransportCredentials: credentials.NewTLS(tlscfg), + sshGRPCCreds, err := auth.NewTransportCredentials(auth.TransportCredentialsConfig{ + TransportCredentials: credentials.NewTLS(sshGRPCTLSConfig), UserGetter: authMiddleware, Authorizer: authorizer, GetAuthPreference: accessPoint.GetAuthPreference, @@ -4691,7 +4823,7 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { // the interceptor. See https://github.com/open-telemetry/opentelemetry-go-contrib/issues/4576. otelgrpc.StreamServerInterceptor(), ), - grpc.Creds(creds), + grpc.Creds(sshGRPCCreds), grpc.MaxConcurrentStreams(defaults.GRPCMaxConcurrentStreams), ) @@ -4805,11 +4937,8 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { if err != nil { return trace.Wrap(err) } + // Register TLS endpoint of the Kube proxy service - tlsConfig, err := conn.ServerTLSConfig(cfg.CipherSuites) - if err != nil { - return trace.Wrap(err) - } component := teleport.Component(teleport.ComponentProxy, teleport.ComponentProxyKube) kubeServiceType := kubeproxy.ProxyService if cfg.Proxy.Kube.LegacyKubeProxy { @@ -4854,10 +4983,12 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { // using Impersonation headers. The upstream service will validate if // the provided connection certificate is from a proxy server and // will impersonate the identity of the user that is making the request. - ConnTLSConfig: tlsConfig.Clone(), - ClusterFeatures: process.GetClusterFeatures, + GetConnTLSCertificate: conn.ClientGetCertificate, + GetConnTLSRoots: conn.ClientGetPool, + ConnTLSCipherSuites: cfg.CipherSuites, + ClusterFeatures: process.GetClusterFeatures, }, - TLS: tlsConfig.Clone(), + TLS: serverTLSConfig.Clone(), LimiterConfig: cfg.Proxy.Limiter, AccessPoint: accessPoint, GetRotation: process.GetRotation, @@ -4907,10 +5038,6 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { if err != nil { return trace.Wrap(err) } - tlsConfig, err := conn.ServerTLSConfig(cfg.CipherSuites) - if err != nil { - return trace.Wrap(err) - } connLimiter, err := limiter.NewLimiter(process.Config.Databases.Limiter) if err != nil { return trace.Wrap(err) @@ -4935,7 +5062,7 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { AccessPoint: accessPoint, Authorizer: authorizer, Tunnel: tsrv, - TLSConfig: tlsConfig, + TLSConfig: serverTLSConfig.Clone(), Limiter: connLimiter, IngressReporter: ingressReporter, ConnectionMonitor: connMonitor, @@ -5006,7 +5133,7 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { if listeners.db.mongo != nil { process.RegisterCriticalFunc("proxy.db.mongo", func() error { logger.InfoContext(process.ExitContext(), "Starting Database Mongo proxy server.", "listen_address", cfg.Proxy.MongoAddr.Addr) - if err := dbProxyServer.ServeMongo(listeners.db.mongo, tlsConfigWeb.Clone()); err != nil { + if err := dbProxyServer.ServeMongo(listeners.db.mongo, tlsConfigWeb); err != nil { logger.WarnContext(process.ExitContext(), "Database Mongo proxy server exited with error.", "error", err) } return nil @@ -5054,13 +5181,9 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { HandlerWithConnInfo: authDialerService.HandleConnection, ForwardTLS: true, }) - identityTLSConf, err := conn.ServerTLSConfig(cfg.CipherSuites) - if err != nil { - return trace.Wrap(err) - } alpnServer, err = alpnproxy.New(alpnproxy.ProxyConfig{ WebTLSConfig: tlsConfigWeb.Clone(), - IdentityTLSConfig: identityTLSConf, + IdentityTLSConfig: serverTLSConfig, Router: alpnRouter, Listener: listeners.alpn, ClusterName: clusterName, @@ -5087,7 +5210,7 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error { if reverseTunnelALPNRouter != nil { reverseTunnelALPNServer, err = alpnproxy.New(alpnproxy.ProxyConfig{ WebTLSConfig: tlsConfigWeb.Clone(), - IdentityTLSConfig: identityTLSConf, + IdentityTLSConfig: serverTLSConfig, Router: reverseTunnelALPNRouter, Listener: listeners.reverseTunnelALPN, ClusterName: clusterName, @@ -5399,10 +5522,8 @@ func (process *TeleportProcess) setupTLSConfigClientCAGeneratorForCluster(tlsCon return nil } -func (process *TeleportProcess) setupALPNTLSConfigForWeb(serverTLSConfig *tls.Config, accessPoint authclient.ReadProxyAccessPoint, clusterName string) (*tls.Config, error) { - tlsConfig := utils.TLSConfig(process.Config.CipherSuites) - tlsConfig.Certificates = serverTLSConfig.Certificates - +func (process *TeleportProcess) setupALPNTLSConfigForWeb(tlsConfig *tls.Config, accessPoint authclient.ReadProxyAccessPoint, clusterName string) (*tls.Config, error) { + tlsConfig = tlsConfig.Clone() setupTLSConfigALPNProtocols(tlsConfig) if err := process.setupTLSConfigClientCAGeneratorForCluster(tlsConfig, accessPoint, clusterName); err != nil { return nil, trace.Wrap(err) @@ -5745,7 +5866,7 @@ func (process *TeleportProcess) initApps() { if err != nil { return trace.Wrap(err) } - tlsConfig, err := conn.ServerTLSConfig(nil) + tlsConfig, err := conn.ServerTLSConfig(process.Config.CipherSuites) if err != nil { return trace.Wrap(err) } @@ -6390,7 +6511,6 @@ func (process *TeleportProcess) initPublicGRPCServer( }) if err != nil { return nil, trace.Wrap(err) - } accessgraphsecretsv1pb.RegisterSecretsScannerServiceServer(server, accessGraphProxySvc) diff --git a/lib/service/service_test.go b/lib/service/service_test.go index c65199403ac9f..4564653ab5d86 100644 --- a/lib/service/service_test.go +++ b/lib/service/service_test.go @@ -41,7 +41,6 @@ import ( "github.com/jonboulle/clockwork" "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" - "golang.org/x/crypto/ssh" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" @@ -49,6 +48,7 @@ import ( "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/breaker" "github.com/gravitational/teleport/api/types" + apiutils "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/entitlements" "github.com/gravitational/teleport/lib" "github.com/gravitational/teleport/lib/auth" @@ -369,9 +369,8 @@ func TestServiceCheckPrincipals(t *testing.T) { require.NoError(t, err) defer tlsServer.Close() - testConnector := &Connector{ - serverIdentity: tlsServer.Identity, - } + testConnector, err := newConnector(tlsServer.Identity, tlsServer.Identity) + require.NoError(t, err) tests := []struct { inPrincipals []string @@ -889,18 +888,8 @@ func TestSetupProxyTLSConfig(t *testing.T) { // Setting Supervisor so that `ExitContext` can be called. Supervisor: NewSupervisor("process-id", cfg.Log), } - conn := &Connector{ - clientIdentity: &state.Identity{}, - serverIdentity: &state.Identity{ - Cert: &ssh.Certificate{ - Permissions: ssh.Permissions{ - Extensions: map[string]string{}, - }, - }, - }, - } tls, err := process.setupProxyTLSConfig( - conn, + &Connector{}, &mockReverseTunnelServer{}, &mockAccessPoint{}, "cluster", @@ -1239,11 +1228,9 @@ func TestProxyGRPCServers(t *testing.T) { serverIdentity, err := auth.NewServerIdentity(testAuthServer.AuthServer, hostID, types.RoleProxy) require.NoError(t, err) - testConnector := &Connector{ - clientIdentity: serverIdentity, - serverIdentity: serverIdentity, - Client: client, - } + testConnector, err := newConnector(serverIdentity, serverIdentity) + require.NoError(t, err) + testConnector.Client = client // Create a listener for the insecure gRPC server. insecureListener, err := net.Listen("tcp", "localhost:0") @@ -1360,10 +1347,19 @@ func TestProxyGRPCServers(t *testing.T) { { name: "secure client to secure server", credentials: func() credentials.TransportCredentials { - // Create a new client using the server identity. - creds, err := testConnector.ServerTLSConfig(nil) - require.NoError(t, err) - return credentials.NewTLS(creds) + // Create a new client using the client identity. + tlsConfig := utils.TLSConfig(nil) + tlsConfig.ServerName = apiutils.EncodeClusterName(testConnector.ClusterName()) + tlsConfig.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) { + tlsCert, err := testConnector.ClientGetCertificate() + if err != nil { + return nil, trace.Wrap(err) + } + return tlsCert, nil + } + tlsConfig.InsecureSkipVerify = true + tlsConfig.VerifyConnection = utils.VerifyConnectionWithRoots(testConnector.ClientGetPool) + return credentials.NewTLS(tlsConfig) }(), listenerAddr: secureListener.Addr().String(), assertErr: require.NoError, diff --git a/lib/services/access_checker.go b/lib/services/access_checker.go index 1ed3e38880e72..b4ccd0cfc0c48 100644 --- a/lib/services/access_checker.go +++ b/lib/services/access_checker.go @@ -259,8 +259,8 @@ type AccessChecker interface { // Supports the following resource types: // // - types.Server with GetKind() == types.KindNode - // // - types.KindWindowsDesktop + // - types.KindApp with IsAWSConsole() == true GetAllowedLoginsForResource(resource AccessCheckable) ([]string, error) // CheckSPIFFESVID checks if the role set has access to generating the @@ -767,8 +767,8 @@ func (a *accessChecker) EnumerateEntities(resource AccessCheckable, listFn roleE // Supports the following resource types: // // - types.Server with GetKind() == types.KindNode -// // - types.KindWindowsDesktop +// - types.KindApp with IsAWSConsole() == true func (a *accessChecker) GetAllowedLoginsForResource(resource AccessCheckable) ([]string, error) { // Create a map indexed by all logins in the RoleSet, // mapped to false if any role has it in its deny section, @@ -1228,14 +1228,15 @@ func AccessInfoFromLocalIdentity(identity tlsca.Identity, access UserGetter) (*A // local roles based on the given roleMap. func AccessInfoFromRemoteIdentity(identity tlsca.Identity, roleMap types.RoleMap) (*AccessInfo, error) { // Set internal traits for the remote user. This allows Teleport to work by - // passing exact logins, Kubernetes users/groups and database users/names - // to the remote cluster. + // passing exact logins, Kubernetes users/groups, database users/names, and + // AWS Role ARNs to the remote cluster. traits := map[string][]string{ - constants.TraitLogins: identity.Principals, - constants.TraitKubeGroups: identity.KubernetesGroups, - constants.TraitKubeUsers: identity.KubernetesUsers, - constants.TraitDBNames: identity.DatabaseNames, - constants.TraitDBUsers: identity.DatabaseUsers, + constants.TraitLogins: identity.Principals, + constants.TraitKubeGroups: identity.KubernetesGroups, + constants.TraitKubeUsers: identity.KubernetesUsers, + constants.TraitDBNames: identity.DatabaseNames, + constants.TraitDBUsers: identity.DatabaseUsers, + constants.TraitAWSRoleARNs: identity.AWSRoleARNs, } // Prior to Teleport 6.2 no user traits were passed to remote clusters // except for the internal ones specified above. diff --git a/lib/services/access_request_cache.go b/lib/services/access_request_cache.go index 1c23bce55d921..277358414f415 100644 --- a/lib/services/access_request_cache.go +++ b/lib/services/access_request_cache.go @@ -352,6 +352,7 @@ func (c *AccessRequestCache) getResourcesAndUpdateCurrent(ctx context.Context) e c.rw.Lock() defer c.rw.Unlock() c.primaryCache = cache + close(c.initC) return nil } @@ -404,6 +405,12 @@ func (c *AccessRequestCache) initializationChan() <-chan struct{} { return c.initC } +// InitializationChan is part of the resourceCollector interface and gets the channel +// used to signal that the accessRequestCache has been initialized. +func (c *AccessRequestCache) InitializationChan() <-chan struct{} { + return c.initializationChan() +} + // Close terminates the background process that keeps the access request cache up to // date, and terminates any inflight load operations. func (c *AccessRequestCache) Close() error { diff --git a/lib/services/access_request_cache_test.go b/lib/services/access_request_cache_test.go index 92c45c4bec4b0..905e2882f97e5 100644 --- a/lib/services/access_request_cache_test.go +++ b/lib/services/access_request_cache_test.go @@ -51,6 +51,12 @@ func newAccessRequestPack(t *testing.T) (accessRequestServices, *services.Access }) require.NoError(t, err) + select { + case <-cache.InitializationChan(): + case <-time.After(time.Second * 30): + require.FailNow(t, "timeout waiting for access request cache to initialize") + } + return svcs, cache } diff --git a/lib/services/saml.go b/lib/services/saml.go index a679c37d0602c..c4bec91d2310d 100644 --- a/lib/services/saml.go +++ b/lib/services/saml.go @@ -41,6 +41,12 @@ import ( "github.com/gravitational/teleport/lib/utils" ) +type SAMLConnectorGetter interface { + GetSAMLConnector(ctx context.Context, id string, withSecrets bool) (types.SAMLConnector, error) +} + +const ErrMsgHowToFixMissingPrivateKey = "You must either specify the singing key pair (obtain the existing one with `tctl get saml --with-secrets`) or let Teleport generate a new one (remove singing_key_pair in the resource you're trying to create)." + // ValidateSAMLConnector validates the SAMLConnector and sets default values. // If a remote to fetch roles is specified, roles will be validated to exist. func ValidateSAMLConnector(sc types.SAMLConnector, rg RoleGetter) error { @@ -330,3 +336,23 @@ func MarshalSAMLConnector(samlConnector types.SAMLConnector, opts ...MarshalOpti return nil, trace.BadParameter("unrecognized SAML connector version %T", samlConnector) } } + +// FillSAMLSigningKeyFromExisting looks up the existing SAML connector and populates the signing key if it's missing. +// This must be called only if the SAML Connector signing key pair has been initialized (ValidateSAMLConnector) and +// the private key is still empty. +func FillSAMLSigningKeyFromExisting(ctx context.Context, connector types.SAMLConnector, sg SAMLConnectorGetter) error { + existing, err := sg.GetSAMLConnector(ctx, connector.GetName(), true /* with secrets */) + switch { + case trace.IsNotFound(err): + return trace.BadParameter("failed to create SAML connector, the SAML connector has no signing key set. " + ErrMsgHowToFixMissingPrivateKey) + case err != nil: + return trace.BadParameter("failed to update SAML connector, the SAML connector has no signing key set and looking up the existing connector failed with the error: %s. %s", err.Error(), ErrMsgHowToFixMissingPrivateKey) + } + + existingSkp := existing.GetSigningKeyPair() + if existingSkp == nil || existingSkp.Cert != connector.GetSigningKeyPair().Cert { + return trace.BadParameter("failed to update the SAML connector, the SAML connector has no signing key and its signing certificate does not match the existing one. " + ErrMsgHowToFixMissingPrivateKey) + } + connector.SetSigningKeyPair(existingSkp) + return nil +} diff --git a/lib/services/saml_test.go b/lib/services/saml_test.go index c73c37b7c87ab..ab060dd218d9f 100644 --- a/lib/services/saml_test.go +++ b/lib/services/saml_test.go @@ -20,8 +20,10 @@ package services import ( "context" + "crypto/x509/pkix" "strings" "testing" + "time" "github.com/gravitational/trace" "github.com/stretchr/testify/require" @@ -30,6 +32,7 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/fixtures" + "github.com/gravitational/teleport/lib/utils" ) func TestParseFromMetadata(t *testing.T) { @@ -127,6 +130,115 @@ func TestValidateRoles(t *testing.T) { } } +type mockSAMLGetter map[string]types.SAMLConnector + +func (m mockSAMLGetter) GetSAMLConnector(_ context.Context, id string, withSecrets bool) (types.SAMLConnector, error) { + connector, ok := m[id] + if !ok { + return nil, trace.NotFound("%s not found", id) + } + return connector, nil +} + +func TestFillSAMLSigningKeyFromExisting(t *testing.T) { + t.Parallel() + + // Test setup: generate the fixtures + existingKeyPEM, existingCertPEM, err := utils.GenerateSelfSignedSigningCert(pkix.Name{ + Organization: []string{"Teleport OSS"}, + CommonName: "teleport.localhost.localdomain", + }, nil, 10*365*24*time.Hour) + require.NoError(t, err) + + existingSkp := &types.AsymmetricKeyPair{ + PrivateKey: string(existingKeyPEM), + Cert: string(existingCertPEM), + } + + existingConnectorName := "existing" + existingConnectors := mockSAMLGetter{ + existingConnectorName: &types.SAMLConnectorV2{ + Spec: types.SAMLConnectorSpecV2{ + SigningKeyPair: existingSkp, + }, + }, + } + + _, unrelatedCertPEM, err := utils.GenerateSelfSignedSigningCert(pkix.Name{ + Organization: []string{"Teleport OSS"}, + CommonName: "teleport.localhost.localdomain", + }, nil, 10*365*24*time.Hour) + require.NoError(t, err) + + // Test setup: define test cases + testCases := []struct { + name string + connectorName string + connectorSpec types.SAMLConnectorSpecV2 + assertErr require.ErrorAssertionFunc + assertResult require.ValueAssertionFunc + }{ + { + name: "should read singing key from existing connector with matching cert", + connectorName: existingConnectorName, + connectorSpec: types.SAMLConnectorSpecV2{ + SigningKeyPair: &types.AsymmetricKeyPair{ + PrivateKey: "", + Cert: string(existingCertPEM), + }, + }, + assertErr: require.NoError, + assertResult: func(t require.TestingT, value interface{}, args ...interface{}) { + require.Implements(t, (*types.SAMLConnector)(nil), value) + connector := value.(types.SAMLConnector) + skp := connector.GetSigningKeyPair() + require.Equal(t, existingSkp, skp) + }, + }, + { + name: "should error when there's no existing connector", + connectorName: "non-existing", + connectorSpec: types.SAMLConnectorSpecV2{ + SigningKeyPair: &types.AsymmetricKeyPair{ + PrivateKey: "", + Cert: string(unrelatedCertPEM), + }, + }, + assertErr: require.Error, + }, + { + name: "should error when existing connector cert is not matching", + connectorName: existingConnectorName, + connectorSpec: types.SAMLConnectorSpecV2{ + SigningKeyPair: &types.AsymmetricKeyPair{ + PrivateKey: "", + Cert: string(unrelatedCertPEM), + }, + }, + assertErr: require.Error, + }, + } + + // Test execution + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + connector := &types.SAMLConnectorV2{ + Metadata: types.Metadata{ + Name: tc.connectorName, + }, + Spec: tc.connectorSpec, + } + + err := FillSAMLSigningKeyFromExisting(ctx, connector, existingConnectors) + tc.assertErr(t, err) + if tc.assertResult != nil { + tc.assertResult(t, connector) + } + }) + } +} + // roleSet is a basic set of roles keyed by role name. It implements the // RoleGetter interface, returning the role if it exists, or a trace.NotFound // error if it does not exist. diff --git a/lib/services/suite/suite.go b/lib/services/suite/suite.go index 0f2db7a978e22..b27f38df4e47f 100644 --- a/lib/services/suite/suite.go +++ b/lib/services/suite/suite.go @@ -87,6 +87,12 @@ func NewTestCAWithConfig(config TestCAConfig) *types.CertAuthorityV2 { // Always use pre-generated RSA key for the db_client CA. keyPEM = fixtures.PEMBytes["rsa-db-client"] } + if config.Type == types.SAMLIDPCA { + // The SAML IdP uses xmldsig RSA SHA256 signature method + // http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 + // to sign the SAML assertion, so the key must be an RSA key. + keyPEM = fixtures.PEMBytes["rsa"] + } if len(config.PrivateKeys) > 0 { // Allow test to override the private key. keyPEM = config.PrivateKeys[0] diff --git a/lib/srv/ctx.go b/lib/srv/ctx.go index f6f682eb61e8b..2c72a7a46b348 100644 --- a/lib/srv/ctx.go +++ b/lib/srv/ctx.go @@ -1237,6 +1237,8 @@ func (id *IdentityContext) GetUserMetadata() apievents.UserMetadata { AccessRequests: id.ActiveRequests, TrustedDevice: eventDeviceMetadataFromCert(id.Certificate), UserKind: userKind, + BotName: id.BotName, + BotInstanceID: id.BotInstanceID, } } diff --git a/lib/srv/ctx_test.go b/lib/srv/ctx_test.go index c9edf04f0baf2..6bc99ca6e7dd3 100644 --- a/lib/srv/ctx_test.go +++ b/lib/srv/ctx_test.go @@ -232,6 +232,22 @@ func TestIdentityContext_GetUserMetadata(t *testing.T) { UserKind: apievents.UserKind_USER_KIND_HUMAN, }, }, + { + name: "bot metadata", + idCtx: IdentityContext{ + TeleportUser: "bot-alpaca", + Login: "alpaca1", + BotName: "alpaca", + BotInstanceID: "123-123-123", + }, + want: apievents.UserMetadata{ + User: "bot-alpaca", + Login: "alpaca1", + UserKind: apievents.UserKind_USER_KIND_BOT, + BotName: "alpaca", + BotInstanceID: "123-123-123", + }, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { diff --git a/lib/srv/db/autousers_test.go b/lib/srv/db/autousers_test.go index bc88a19f78f5b..279ce900f1664 100644 --- a/lib/srv/db/autousers_test.go +++ b/lib/srv/db/autousers_test.go @@ -177,6 +177,8 @@ func TestAutoUsersPostgres(t *testing.T) { // 2. If there are any database permissions: admin connecting to session database. if len(tc.databasePermissions) > 0 { + // expect two connections to be made. + requirePostgresConnection(t, testCtx.postgres["postgres"].db.ParametersCh(), "postgres", "user-db") requirePostgresConnection(t, testCtx.postgres["postgres"].db.ParametersCh(), "postgres", "user-db") } diff --git a/lib/srv/db/cloud/gcp.go b/lib/srv/db/cloud/gcp.go index 30b6f1aa168f3..91d5a7b3a7a62 100644 --- a/lib/srv/db/cloud/gcp.go +++ b/lib/srv/db/cloud/gcp.go @@ -21,9 +21,11 @@ package cloud import ( "context" "crypto/tls" + "time" "github.com/gravitational/trace" + "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/cloud/gcp" "github.com/gravitational/teleport/lib/srv/db/common" ) @@ -31,8 +33,8 @@ import ( // GetGCPRequireSSL requests settings for the project/instance in session from GCP // and returns true when the instance requires SSL. An access denied error is // returned when an unauthorized error is returned from GCP. -func GetGCPRequireSSL(ctx context.Context, sessionCtx *common.Session, gcpClient gcp.SQLAdminClient) (requireSSL bool, err error) { - dbi, err := gcpClient.GetDatabaseInstance(ctx, sessionCtx.Database) +func GetGCPRequireSSL(ctx context.Context, database types.Database, gcpClient gcp.SQLAdminClient) (requireSSL bool, err error) { + dbi, err := gcpClient.GetDatabaseInstance(ctx, database) if err != nil { err = common.ConvertError(err) if trace.IsAccessDenied(err) { @@ -43,9 +45,9 @@ func GetGCPRequireSSL(ctx context.Context, sessionCtx *common.Session, gcpClient Make sure Teleport db service has "Cloud SQL Admin" GCP IAM role, or "cloudsql.instances.get" IAM permission.`, err) } - return false, trace.Wrap(err, "Failed to get Cloud SQL instance information for %q.", sessionCtx.Database.GetGCP().GetServerName()) + return false, trace.Wrap(err, "Failed to get Cloud SQL instance information for %q.", database.GetGCP().GetServerName()) } else if dbi.Settings == nil || dbi.Settings.IpConfiguration == nil { - return false, trace.BadParameter("Failed to find Cloud SQL settings for %q. GCP returned %+v.", sessionCtx.Database.GetGCP().GetServerName(), dbi) + return false, trace.BadParameter("Failed to find Cloud SQL settings for %q. GCP returned %+v.", database.GetGCP().GetServerName(), dbi) } return dbi.Settings.IpConfiguration.RequireSsl, nil } @@ -53,8 +55,8 @@ or "cloudsql.instances.get" IAM permission.`, err) // AppendGCPClientCert calls the GCP API to generate an ephemeral certificate // and adds it to the TLS config. An access denied error is returned when the // generate call fails. -func AppendGCPClientCert(ctx context.Context, sessionCtx *common.Session, gcpClient gcp.SQLAdminClient, tlsConfig *tls.Config) error { - cert, err := gcpClient.GenerateEphemeralCert(ctx, sessionCtx.Database, sessionCtx.Identity) +func AppendGCPClientCert(ctx context.Context, certExpiry time.Time, database types.Database, gcpClient gcp.SQLAdminClient, tlsConfig *tls.Config) error { + cert, err := gcpClient.GenerateEphemeralCert(ctx, database, certExpiry) if err != nil { err = common.ConvertError(err) if trace.IsAccessDenied(err) { @@ -65,7 +67,7 @@ func AppendGCPClientCert(ctx context.Context, sessionCtx *common.Session, gcpCli Make sure Teleport db service has "Cloud SQL Admin" GCP IAM role, or "cloudsql.sslCerts.createEphemeral" IAM permission.`, err) } - return trace.Wrap(err, "Failed to generate GCP ephemeral client certificate for %q.", sessionCtx.Database.GetGCP().GetServerName()) + return trace.Wrap(err, "Failed to generate GCP ephemeral client certificate for %q.", database.GetGCP().GetServerName()) } tlsConfig.Certificates = []tls.Certificate{*cert} return nil diff --git a/lib/srv/db/common/databaseobjectimportrule/apply.go b/lib/srv/db/common/databaseobjectimportrule/apply.go index 5fed6382cd7bf..2755e8c87d191 100644 --- a/lib/srv/db/common/databaseobjectimportrule/apply.go +++ b/lib/srv/db/common/databaseobjectimportrule/apply.go @@ -36,30 +36,21 @@ import ( "github.com/gravitational/teleport/lib/utils/typical" ) -// ApplyDatabaseObjectImportRules applies the given set of rules onto a set of objects coming from a same database. -// Returns a fresh copy of a subset of supplied objects, filtered and modified. -// For the object to be returned, it must match at least one rule. -// The modification consists of application of extra labels, per matching mappings. -// If there are any errors due to invalid label template, the corresponding objects will be dropped. -// Final error count is returned. +// ApplyDatabaseObjectImportRules applies the specified set of rules to a collection of objects from the same database. +// It returns a new copy of a subset of the provided objects, filtered and labeled according to the rules. +// An object is included in the return set only if it matches at least one rule. +// Objects with errors due to invalid label templates are excluded. +// The function returns the final count of errors encountered. func ApplyDatabaseObjectImportRules(ctx context.Context, logger *slog.Logger, rules []*dbobjectimportrulev1.DatabaseObjectImportRule, database types.Database, objs []*dbobjectv1.DatabaseObject) ([]*dbobjectv1.DatabaseObject, int) { // sort: rules with higher priorities are applied last. sort.Slice(rules, func(i, j int) bool { return rules[i].Spec.Priority < rules[j].Spec.Priority }) - // filter rules: keep those with matching labels - // we only need mappings from the rules, so extract those. + // get mappings from rules matching database labels var mappings []*dbobjectimportrulev1.DatabaseObjectImportRuleMapping - for _, rule := range rules { - dbLabels := make(types.Labels) - mapLabel := label.ToMap(rule.Spec.GetDatabaseLabels()) - for k, v := range mapLabel { - dbLabels[k] = v - } - if ok, _, _ := services.MatchLabels(dbLabels, database.GetAllLabels()); ok { - mappings = append(mappings, rule.Spec.Mappings...) - } + for _, rule := range filterRulesForDatabase(rules, database) { + mappings = append(mappings, rule.Spec.Mappings...) } var objects []*dbobjectv1.DatabaseObject @@ -101,6 +92,47 @@ func ApplyDatabaseObjectImportRules(ctx context.Context, logger *slog.Logger, ru return objects, errCount } +type DbNameFilter func(string) bool + +// CalculateDatabaseNameFilter returns a function that checks if the given database name will be accepted by any of the rules. +// This can be used to skip the import from the given database if we can tell already that none of the objects will be accepted. +func CalculateDatabaseNameFilter(rules []*dbobjectimportrulev1.DatabaseObjectImportRule, database types.Database) DbNameFilter { + var patterns []string + for _, rule := range filterRulesForDatabase(rules, database) { + spec := rule.GetSpec() + for _, mapping := range spec.Mappings { + names := mapping.GetScope().GetDatabaseNames() + // empty list of database names means "any database name". + if len(names) == 0 { + return func(_ string) bool { + return true + } + } + patterns = append(patterns, names...) + } + } + return func(dbName string) bool { + return matchAny(patterns, dbName) + } +} + +// filterRulesForDatabase returns the set of rules whose labels match the database used as an argument. +func filterRulesForDatabase(rules []*dbobjectimportrulev1.DatabaseObjectImportRule, database types.Database) []*dbobjectimportrulev1.DatabaseObjectImportRule { + var out []*dbobjectimportrulev1.DatabaseObjectImportRule + for _, rule := range rules { + dbLabels := make(types.Labels) + mapLabel := label.ToMap(rule.Spec.GetDatabaseLabels()) + for k, v := range mapLabel { + dbLabels[k] = v + } + + if ok, _, _ := services.MatchLabels(dbLabels, database.GetAllLabels()); ok { + out = append(out, rule) + } + } + return out +} + // validateTemplate evaluates the template, checking for potential errors. func validateTemplate(template string) error { _, err := evalTemplate(template, &dbobjectv1.DatabaseObjectSpec{}) diff --git a/lib/srv/db/common/databaseobjectimportrule/apply_test.go b/lib/srv/db/common/databaseobjectimportrule/apply_test.go index e553be9cb1e01..7f4096bb908d5 100644 --- a/lib/srv/db/common/databaseobjectimportrule/apply_test.go +++ b/lib/srv/db/common/databaseobjectimportrule/apply_test.go @@ -22,6 +22,7 @@ import ( "maps" "testing" + "github.com/google/uuid" "github.com/stretchr/testify/require" "github.com/gravitational/teleport/api/defaults" @@ -33,51 +34,54 @@ import ( "github.com/gravitational/teleport/lib/srv/db/common/databaseobject" ) -func TestApplyDatabaseObjectImportRules(t *testing.T) { - mkDatabase := func(name string, labels map[string]string) *types.DatabaseV3 { - db, err := types.NewDatabaseV3(types.Metadata{ - Name: name, - Labels: labels, - }, types.DatabaseSpecV3{ - Protocol: "postgres", - URI: "localhost:5252", - }) - require.NoError(t, err) - return db - } - - type option func(db *dbobjectv1.DatabaseObject) error +func mkDatabase(t *testing.T, name string, labels map[string]string) *types.DatabaseV3 { + t.Helper() + db, err := types.NewDatabaseV3(types.Metadata{ + Name: name, + Labels: labels, + }, types.DatabaseSpecV3{ + Protocol: "postgres", + URI: "localhost:5252", + }) + require.NoError(t, err) + return db +} - mkDatabaseObject := func(name string, spec *dbobjectv1.DatabaseObjectSpec, options ...option) *dbobjectv1.DatabaseObject { - spec.Name = name - out, err := databaseobject.NewDatabaseObject(name, spec) - require.NoError(t, err) - for _, opt := range options { - require.NoError(t, opt(out)) - } +type option func(db *dbobjectv1.DatabaseObject) error - return out +func mkDatabaseObject(t *testing.T, name string, spec *dbobjectv1.DatabaseObjectSpec, options ...option) *dbobjectv1.DatabaseObject { + t.Helper() + spec.Name = name + out, err := databaseobject.NewDatabaseObject(name, spec) + require.NoError(t, err) + for _, opt := range options { + require.NoError(t, opt(out)) } - mkImportRule := func(name string, spec *databaseobjectimportrulev1.DatabaseObjectImportRuleSpec) *databaseobjectimportrulev1.DatabaseObjectImportRule { - out, err := NewDatabaseObjectImportRule(name, spec) - require.NoError(t, err) - return out - } + return out +} - mkImportRuleNoValidation := func(name string, spec *databaseobjectimportrulev1.DatabaseObjectImportRuleSpec) *databaseobjectimportrulev1.DatabaseObjectImportRule { - out := &databaseobjectimportrulev1.DatabaseObjectImportRule{ - Kind: types.KindDatabaseObjectImportRule, - Version: types.V1, - Metadata: &headerv1.Metadata{ - Name: name, - Namespace: defaults.Namespace, - }, - Spec: spec, - } - return out +func mkImportRule(t *testing.T, name string, spec *databaseobjectimportrulev1.DatabaseObjectImportRuleSpec) *databaseobjectimportrulev1.DatabaseObjectImportRule { + t.Helper() + out, err := NewDatabaseObjectImportRule(name, spec) + require.NoError(t, err) + return out +} + +func mkImportRuleNoValidation(name string, spec *databaseobjectimportrulev1.DatabaseObjectImportRuleSpec) *databaseobjectimportrulev1.DatabaseObjectImportRule { + out := &databaseobjectimportrulev1.DatabaseObjectImportRule{ + Kind: types.KindDatabaseObjectImportRule, + Version: types.V1, + Metadata: &headerv1.Metadata{ + Name: name, + Namespace: defaults.Namespace, + }, + Spec: spec, } + return out +} +func TestApplyDatabaseObjectImportRules(t *testing.T) { tests := []struct { name string rules []*databaseobjectimportrulev1.DatabaseObjectImportRule @@ -89,14 +93,14 @@ func TestApplyDatabaseObjectImportRules(t *testing.T) { { name: "empty inputs", rules: []*databaseobjectimportrulev1.DatabaseObjectImportRule{}, - database: mkDatabase("dummy", map[string]string{"env": "prod"}), + database: mkDatabase(t, "dummy", map[string]string{"env": "prod"}), objs: nil, want: nil, }, { name: "database labels are matched by the rules", rules: []*databaseobjectimportrulev1.DatabaseObjectImportRule{ - mkImportRule("foo", &databaseobjectimportrulev1.DatabaseObjectImportRuleSpec{ + mkImportRule(t, "foo", &databaseobjectimportrulev1.DatabaseObjectImportRuleSpec{ Priority: 10, DatabaseLabels: label.FromMap(map[string][]string{"env": {"dev"}}), Mappings: []*databaseobjectimportrulev1.DatabaseObjectImportRuleMapping{ @@ -111,7 +115,7 @@ func TestApplyDatabaseObjectImportRules(t *testing.T) { }, }, }), - mkImportRule("bar", &databaseobjectimportrulev1.DatabaseObjectImportRuleSpec{ + mkImportRule(t, "bar", &databaseobjectimportrulev1.DatabaseObjectImportRuleSpec{ Priority: 20, DatabaseLabels: label.FromMap(map[string][]string{"env": {"prod"}}), Mappings: []*databaseobjectimportrulev1.DatabaseObjectImportRuleMapping{ @@ -127,12 +131,12 @@ func TestApplyDatabaseObjectImportRules(t *testing.T) { }, }), }, - database: mkDatabase("dummy", map[string]string{"env": "prod"}), + database: mkDatabase(t, "dummy", map[string]string{"env": "prod"}), objs: []*dbobjectv1.DatabaseObject{ - mkDatabaseObject("foo", &dbobjectv1.DatabaseObjectSpec{ObjectKind: ObjectKindTable, Protocol: "postgres"}), + mkDatabaseObject(t, "foo", &dbobjectv1.DatabaseObjectSpec{ObjectKind: ObjectKindTable, Protocol: "postgres"}), }, want: []*dbobjectv1.DatabaseObject{ - mkDatabaseObject("foo", &dbobjectv1.DatabaseObjectSpec{ObjectKind: ObjectKindTable, Protocol: "postgres"}, + mkDatabaseObject(t, "foo", &dbobjectv1.DatabaseObjectSpec{ObjectKind: ObjectKindTable, Protocol: "postgres"}, func(db *dbobjectv1.DatabaseObject) error { db.Metadata.Labels = map[string]string{ "dev_access": "ro", @@ -145,7 +149,7 @@ func TestApplyDatabaseObjectImportRules(t *testing.T) { { name: "rule priorities are applied", rules: []*databaseobjectimportrulev1.DatabaseObjectImportRule{ - mkImportRule("foo", &databaseobjectimportrulev1.DatabaseObjectImportRuleSpec{ + mkImportRule(t, "foo", &databaseobjectimportrulev1.DatabaseObjectImportRuleSpec{ Priority: 10, DatabaseLabels: label.FromMap(map[string][]string{"*": {"*"}}), Mappings: []*databaseobjectimportrulev1.DatabaseObjectImportRuleMapping{ @@ -161,7 +165,7 @@ func TestApplyDatabaseObjectImportRules(t *testing.T) { }, }), - mkImportRule("bar", &databaseobjectimportrulev1.DatabaseObjectImportRuleSpec{ + mkImportRule(t, "bar", &databaseobjectimportrulev1.DatabaseObjectImportRuleSpec{ Priority: 20, DatabaseLabels: label.FromMap(map[string][]string{"*": {"*"}}), Mappings: []*databaseobjectimportrulev1.DatabaseObjectImportRuleMapping{ @@ -177,12 +181,12 @@ func TestApplyDatabaseObjectImportRules(t *testing.T) { }, }), }, - database: mkDatabase("dummy", map[string]string{}), + database: mkDatabase(t, "dummy", map[string]string{}), objs: []*dbobjectv1.DatabaseObject{ - mkDatabaseObject("foo", &dbobjectv1.DatabaseObjectSpec{ObjectKind: ObjectKindTable, Protocol: "postgres"}), + mkDatabaseObject(t, "foo", &dbobjectv1.DatabaseObjectSpec{ObjectKind: ObjectKindTable, Protocol: "postgres"}), }, want: []*dbobjectv1.DatabaseObject{ - mkDatabaseObject("foo", &dbobjectv1.DatabaseObjectSpec{ObjectKind: ObjectKindTable, Protocol: "postgres"}, func(db *dbobjectv1.DatabaseObject) error { + mkDatabaseObject(t, "foo", &dbobjectv1.DatabaseObjectSpec{ObjectKind: ObjectKindTable, Protocol: "postgres"}, func(db *dbobjectv1.DatabaseObject) error { db.Metadata.Labels = map[string]string{ "dev_access": "ro", "flag_from_dev": "dummy", @@ -195,7 +199,7 @@ func TestApplyDatabaseObjectImportRules(t *testing.T) { { name: "errors are counted", rules: []*databaseobjectimportrulev1.DatabaseObjectImportRule{ - mkImportRule("foo", &databaseobjectimportrulev1.DatabaseObjectImportRuleSpec{ + mkImportRule(t, "foo", &databaseobjectimportrulev1.DatabaseObjectImportRuleSpec{ Priority: 10, DatabaseLabels: label.FromMap(map[string][]string{"*": {"*"}}), Mappings: []*databaseobjectimportrulev1.DatabaseObjectImportRuleMapping{ @@ -231,14 +235,14 @@ func TestApplyDatabaseObjectImportRules(t *testing.T) { }, }), }, - database: mkDatabase("dummy", map[string]string{}), + database: mkDatabase(t, "dummy", map[string]string{}), objs: []*dbobjectv1.DatabaseObject{ - mkDatabaseObject("foo", &dbobjectv1.DatabaseObjectSpec{ObjectKind: ObjectKindTable, Protocol: "postgres"}), - mkDatabaseObject("bar", &dbobjectv1.DatabaseObjectSpec{ObjectKind: ObjectKindTable, Protocol: "postgres"}), - mkDatabaseObject("baz", &dbobjectv1.DatabaseObjectSpec{ObjectKind: ObjectKindTable, Protocol: "postgres"}), + mkDatabaseObject(t, "foo", &dbobjectv1.DatabaseObjectSpec{ObjectKind: ObjectKindTable, Protocol: "postgres"}), + mkDatabaseObject(t, "bar", &dbobjectv1.DatabaseObjectSpec{ObjectKind: ObjectKindTable, Protocol: "postgres"}), + mkDatabaseObject(t, "baz", &dbobjectv1.DatabaseObjectSpec{ObjectKind: ObjectKindTable, Protocol: "postgres"}), }, want: []*dbobjectv1.DatabaseObject{ - mkDatabaseObject("foo", &dbobjectv1.DatabaseObjectSpec{ObjectKind: ObjectKindTable, Protocol: "postgres"}, func(db *dbobjectv1.DatabaseObject) error { + mkDatabaseObject(t, "foo", &dbobjectv1.DatabaseObjectSpec{ObjectKind: ObjectKindTable, Protocol: "postgres"}, func(db *dbobjectv1.DatabaseObject) error { db.Metadata.Labels = map[string]string{ "dev_access": "ro", "flag_from_dev": "dummy", @@ -704,3 +708,142 @@ func Test_splitExpression(t *testing.T) { }) } } + +func TestFilterRulesForDatabase(t *testing.T) { + tests := []struct { + name string + rules []*databaseobjectimportrulev1.DatabaseObjectImportRule + + want []*databaseobjectimportrulev1.DatabaseObjectImportRule + }{ + { + name: "all matching", + rules: []*databaseobjectimportrulev1.DatabaseObjectImportRule{ + mkImportRuleNoValidation("rule1", &databaseobjectimportrulev1.DatabaseObjectImportRuleSpec{ + Priority: 10, + DatabaseLabels: label.FromMap(map[string][]string{"env": {"prod"}}), + }), + mkImportRuleNoValidation("rule2", &databaseobjectimportrulev1.DatabaseObjectImportRuleSpec{ + Priority: 20, + DatabaseLabels: label.FromMap(map[string][]string{"env": {"prod"}}), + }), + }, + want: []*databaseobjectimportrulev1.DatabaseObjectImportRule{ + mkImportRuleNoValidation("rule1", &databaseobjectimportrulev1.DatabaseObjectImportRuleSpec{ + Priority: 10, + DatabaseLabels: label.FromMap(map[string][]string{"env": {"prod"}}), + }), + mkImportRuleNoValidation("rule2", &databaseobjectimportrulev1.DatabaseObjectImportRuleSpec{ + Priority: 20, + DatabaseLabels: label.FromMap(map[string][]string{"env": {"prod"}}), + }), + }, + }, + { + name: "one matching", + rules: []*databaseobjectimportrulev1.DatabaseObjectImportRule{ + mkImportRuleNoValidation("rule1", &databaseobjectimportrulev1.DatabaseObjectImportRuleSpec{ + Priority: 10, + DatabaseLabels: label.FromMap(map[string][]string{"env": {"prod"}}), + }), + mkImportRuleNoValidation("rule2", &databaseobjectimportrulev1.DatabaseObjectImportRuleSpec{ + Priority: 20, + DatabaseLabels: label.FromMap(map[string][]string{"env": {"dev"}}), + }), + }, + want: []*databaseobjectimportrulev1.DatabaseObjectImportRule{ + mkImportRuleNoValidation("rule1", &databaseobjectimportrulev1.DatabaseObjectImportRuleSpec{ + Priority: 10, + DatabaseLabels: label.FromMap(map[string][]string{"env": {"prod"}}), + }), + }, + }, + { + name: "empty rules", + rules: []*databaseobjectimportrulev1.DatabaseObjectImportRule{}, + want: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + database := mkDatabase(t, "testdb", map[string]string{"env": "prod"}) + result := filterRulesForDatabase(tt.rules, database) + require.Equal(t, tt.want, result) + }) + } +} + +func TestCalculateDatabaseNameFilter(t *testing.T) { + tests := []struct { + name string + rules []*databaseobjectimportrulev1.DatabaseObjectImportRule + database *types.DatabaseV3 + dbNames map[string]bool + }{ + { + name: "accept any database", + rules: []*databaseobjectimportrulev1.DatabaseObjectImportRule{ + mkImportRule(t, "rule1", &databaseobjectimportrulev1.DatabaseObjectImportRuleSpec{ + DatabaseLabels: label.FromMap(map[string][]string{"*": {"*"}}), + Mappings: []*databaseobjectimportrulev1.DatabaseObjectImportRuleMapping{ + { + Scope: &databaseobjectimportrulev1.DatabaseObjectImportScope{ + // empty list => match any database name. + DatabaseNames: []string{}, + }, + }, + }, + }), + }, + database: mkDatabase(t, "testdb", map[string]string{"env": "prod"}), + dbNames: map[string]bool{"random-name-" + uuid.New().String(): true}, + }, + { + name: "match specific database name", + rules: []*databaseobjectimportrulev1.DatabaseObjectImportRule{ + mkImportRule(t, "rule1", &databaseobjectimportrulev1.DatabaseObjectImportRuleSpec{ + DatabaseLabels: label.FromMap(map[string][]string{"*": {"*"}}), + Mappings: []*databaseobjectimportrulev1.DatabaseObjectImportRuleMapping{ + { + Scope: &databaseobjectimportrulev1.DatabaseObjectImportScope{ + DatabaseNames: []string{"testdb", "devdb"}, + }, + }, + }, + }), + }, + database: mkDatabase(t, "testdb", map[string]string{"env": "prod"}), + dbNames: map[string]bool{"testdb": true, "devdb": true, "baddb": false}, + }, + { + name: "no matching rules", + rules: []*databaseobjectimportrulev1.DatabaseObjectImportRule{ + mkImportRule(t, "rule1", &databaseobjectimportrulev1.DatabaseObjectImportRuleSpec{ + DatabaseLabels: label.FromMap(map[string][]string{"env": {"dev"}}), // env:dev does not match env:prod below. + Mappings: []*databaseobjectimportrulev1.DatabaseObjectImportRuleMapping{ + { + Scope: &databaseobjectimportrulev1.DatabaseObjectImportScope{ + DatabaseNames: []string{"devdb"}, + }, + }, + }, + }), + }, + database: mkDatabase(t, "testdb", map[string]string{"env": "prod"}), + dbNames: map[string]bool{"testdb": false}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + filter := CalculateDatabaseNameFilter(tt.rules, tt.database) + for dbName, expected := range tt.dbNames { + t.Run(dbName, func(tt *testing.T) { + result := filter(dbName) + require.Equal(t, expected, result) + }) + } + }) + } +} diff --git a/lib/srv/db/mysql/engine.go b/lib/srv/db/mysql/engine.go index 9de13625d7a58..4355c836f3f54 100644 --- a/lib/srv/db/mysql/engine.go +++ b/lib/srv/db/mysql/engine.go @@ -248,7 +248,7 @@ func (e *Engine) connect(ctx context.Context, sessionCtx *common.Session) (*clie // Detect whether the instance is set to require SSL. // Fallback to not requiring SSL for access denied errors. - requireSSL, err := cloud.GetGCPRequireSSL(ctx, sessionCtx, gcpClient) + requireSSL, err := cloud.GetGCPRequireSSL(ctx, sessionCtx.Database, gcpClient) if err != nil && !trace.IsAccessDenied(err) { return nil, trace.Wrap(err) } @@ -256,7 +256,7 @@ func (e *Engine) connect(ctx context.Context, sessionCtx *common.Session) (*clie // the instance requires SSL. Also use a TLS dialer instead of // the default net dialer when GCP requires SSL. if requireSSL { - err = cloud.AppendGCPClientCert(ctx, sessionCtx, gcpClient, tlsConfig) + err = cloud.AppendGCPClientCert(ctx, sessionCtx.GetExpiry(), sessionCtx.Database, gcpClient, tlsConfig) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/vnet/daemon/protocol_darwin.m b/lib/srv/db/objects/errors.go similarity index 51% rename from lib/vnet/daemon/protocol_darwin.m rename to lib/srv/db/objects/errors.go index f00e4948ef0c1..8d1296ce2ab90 100644 --- a/lib/vnet/daemon/protocol_darwin.m +++ b/lib/srv/db/objects/errors.go @@ -1,6 +1,3 @@ -//go:build vnetdaemon -// +build vnetdaemon - // Teleport // Copyright (C) 2024 Gravitational, Inc. // @@ -17,6 +14,27 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const char* const VNEErrorDomain = "com.Gravitational.Vnet.ErrorDomain"; +package objects + +import "errors" + +// ErrFetcherDisabled is a custom error that can be returned from fetcher constructor that will be reported with lower severity. +type ErrFetcherDisabled struct { + reason string +} + +func (e ErrFetcherDisabled) Error() string { + return e.reason +} + +// NewErrFetcherDisabled returns a new instance of ErrFetcherDisabled error. +func NewErrFetcherDisabled(reason string) *ErrFetcherDisabled { + return &ErrFetcherDisabled{reason: reason} +} -const int VNEAlreadyRunningError = 1; +// IsErrFetcherDisabled returns true if the error is ErrFetcherDisabled. +func IsErrFetcherDisabled(err error) (bool, string) { + other := &ErrFetcherDisabled{} + matched := errors.As(err, &other) + return matched, other.reason +} diff --git a/lib/srv/db/objects/errors_test.go b/lib/srv/db/objects/errors_test.go new file mode 100644 index 0000000000000..8317fd0b533c5 --- /dev/null +++ b/lib/srv/db/objects/errors_test.go @@ -0,0 +1,73 @@ +// 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 objects + +import ( + "errors" + "testing" + + "github.com/gravitational/trace" + "github.com/stretchr/testify/require" +) + +func TestIsErrFetcherDisabled(t *testing.T) { + tests := []struct { + name string + err error + expectedMatch bool + expectedReason string + }{ + { + name: "nil err", + err: nil, + expectedMatch: false, + expectedReason: "", + }, + { + name: "err not ErrFetcherDisabled", + err: errors.New("some other reason"), + expectedMatch: false, + expectedReason: "", + }, + { + name: "ErrFetcherDisabled with empty reason", + err: NewErrFetcherDisabled(""), + expectedMatch: true, + expectedReason: "", + }, + { + name: "ErrFetcherDisabled bare", + err: NewErrFetcherDisabled("dummy"), + expectedMatch: true, + expectedReason: "dummy", + }, + { + name: "ErrFetcherDisabled wrapped", + err: trace.Wrap(NewErrFetcherDisabled("dummy reason")), + expectedMatch: true, + expectedReason: "dummy reason", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + match, reason := IsErrFetcherDisabled(tc.err) + require.Equal(t, tc.expectedMatch, match) + require.Equal(t, tc.expectedReason, reason) + }) + } +} diff --git a/lib/srv/db/objects/fetcher.go b/lib/srv/db/objects/fetcher.go new file mode 100644 index 0000000000000..676c2b822b320 --- /dev/null +++ b/lib/srv/db/objects/fetcher.go @@ -0,0 +1,152 @@ +// 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 objects + +import ( + "context" + "log/slog" + "sync" + + "github.com/gravitational/trace" + + dbobjectv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/dbobject/v1" + dbobjectimportrulev1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/dbobjectimportrule/v1" + "github.com/gravitational/teleport/api/types" + libcloud "github.com/gravitational/teleport/lib/cloud" + "github.com/gravitational/teleport/lib/srv/db/common" + "github.com/gravitational/teleport/lib/srv/db/common/databaseobjectimportrule" +) + +// ImportRulesReader provides a method to get current set of import rules. +type ImportRulesReader interface { + GetDatabaseObjectImportRules(ctx context.Context) ([]*dbobjectimportrulev1.DatabaseObjectImportRule, error) +} + +// ObjectFetcherConfig provides static object fetcher configuration. +type ObjectFetcherConfig struct { + ImportRules ImportRulesReader + Auth common.Auth + CloudClients libcloud.Clients + Log *slog.Logger +} + +// ObjectFetcher defines an interface for retrieving database objects. +type ObjectFetcher interface { + // FetchAll fetches objects from all databases whose names are accepted by dbNameFilter. + FetchAll(ctx context.Context, dbNameFilter databaseobjectimportrule.DbNameFilter) (map[string]FetchResult, error) + // FetchOneDatabase fetches all objects from a single named database. + FetchOneDatabase(ctx context.Context, dbName string) ([]*dbobjectv1.DatabaseObject, error) +} + +// FetchResult contains fetch result for a single database. +type FetchResult struct { + Objects []*dbobjectv1.DatabaseObject + Error error +} + +// ObjectFetcherFn is a database object fetcher constructor. +type ObjectFetcherFn = func(ctx context.Context, db types.Database, cfg ObjectFetcherConfig) (ObjectFetcher, error) + +var ( + objectFetchers = make(map[string]ObjectFetcherFn) + objectFetchersMutex sync.RWMutex +) + +// RegisterObjectFetcher registers a new object fetcher constructor. +func RegisterObjectFetcher(fn ObjectFetcherFn, names ...string) { + objectFetchersMutex.Lock() + defer objectFetchersMutex.Unlock() + for _, name := range names { + objectFetchers[name] = fn + } +} + +// GetObjectFetcher returns a new object fetcher for given database, respecting global import rules. +func GetObjectFetcher(ctx context.Context, db types.Database, fetcherConfig ObjectFetcherConfig) (ObjectFetcher, error) { + name := db.GetProtocol() + objectFetchersMutex.RLock() + constructor, found := objectFetchers[name] + objectFetchersMutex.RUnlock() + + if !found { + return nil, trace.NotImplemented("fetcher not implemented for protocol %q", name) + } + + fetcher, err := constructor(ctx, db, fetcherConfig) + if err != nil { + return nil, trace.Wrap(err) + } + + // return wrapped fetcher to apply the object import rules. + return &applyRulesFetcher{ + cfg: fetcherConfig, + database: db, + innerFetcher: fetcher, + }, nil +} + +// applyRulesFetcher wraps an existing object fetcher and applies the import rules. +type applyRulesFetcher struct { + cfg ObjectFetcherConfig + database types.Database + innerFetcher ObjectFetcher +} + +func (a *applyRulesFetcher) FetchAll(ctx context.Context, dbNameFilter databaseobjectimportrule.DbNameFilter) (map[string]FetchResult, error) { + rules, err := a.cfg.ImportRules.GetDatabaseObjectImportRules(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + filterFromRules := databaseobjectimportrule.CalculateDatabaseNameFilter(rules, a.database) + fetched, err := a.innerFetcher.FetchAll(ctx, func(dbName string) bool { return dbNameFilter(dbName) && filterFromRules(dbName) }) + if err != nil { + return nil, trace.Wrap(err) + } + + out := make(map[string]FetchResult) + for dbName, result := range fetched { + out[dbName] = FetchResult{ + Objects: a.transform(ctx, dbName, rules, result.Objects), + Error: result.Error, + } + } + + return out, nil +} + +func (a *applyRulesFetcher) FetchOneDatabase(ctx context.Context, dbName string) ([]*dbobjectv1.DatabaseObject, error) { + rules, err := a.cfg.ImportRules.GetDatabaseObjectImportRules(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + fetched, err := a.innerFetcher.FetchOneDatabase(ctx, dbName) + return a.transform(ctx, dbName, rules, fetched), trace.Wrap(err) +} + +func (a *applyRulesFetcher) transform(ctx context.Context, dbName string, rules []*dbobjectimportrulev1.DatabaseObjectImportRule, fetched []*dbobjectv1.DatabaseObject) []*dbobjectv1.DatabaseObject { + transformed, errCount := databaseobjectimportrule.ApplyDatabaseObjectImportRules(ctx, a.cfg.Log, rules, a.database, fetched) + if errCount > 0 { + a.cfg.Log.WarnContext(ctx, "Failed to apply import rules to some objects.", + "db_name", dbName, + "error_count", errCount, + "transformed", len(transformed), + "fetched", len(fetched), + ) + } + return transformed +} diff --git a/lib/srv/db/objects/fetcher_test.go b/lib/srv/db/objects/fetcher_test.go new file mode 100644 index 0000000000000..82cfa8ffd05de --- /dev/null +++ b/lib/srv/db/objects/fetcher_test.go @@ -0,0 +1,94 @@ +// 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 objects + +import ( + "context" + "testing" + + "github.com/google/uuid" + "github.com/gravitational/trace" + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/api/types" +) + +func TestGetObjectFetcher(t *testing.T) { + type dummyObjectFetcher struct{ ObjectFetcher } + + tests := []struct { + name string + getFetcher ObjectFetcherFn + expectFunc func(t *testing.T, fetcher ObjectFetcher, err error) + }{ + { + name: "valid configuration", + getFetcher: func(ctx context.Context, db types.Database, cfg ObjectFetcherConfig) (ObjectFetcher, error) { + return &dummyObjectFetcher{}, nil + }, + expectFunc: func(t *testing.T, fetcher ObjectFetcher, err error) { + require.NoError(t, err) + rulesFetcher, ok := fetcher.(*applyRulesFetcher) + require.True(t, ok) + require.IsType(t, &dummyObjectFetcher{}, rulesFetcher.innerFetcher) + }, + }, + { + name: "error returned from constructor", + getFetcher: func(ctx context.Context, db types.Database, cfg ObjectFetcherConfig) (ObjectFetcher, error) { + return nil, trace.BadParameter("having a bad day, sorry") + }, + expectFunc: func(t *testing.T, fetcher ObjectFetcher, err error) { + require.ErrorContains(t, err, "having a bad day, sorry") + }, + }, + { + name: "unsupported protocol", + expectFunc: func(t *testing.T, fetcher ObjectFetcher, err error) { + require.ErrorContains(t, err, "fetcher not implemented for protocol") + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + + fakeProto := "fakeProto-" + uuid.New().String() + if tt.getFetcher != nil { + RegisterObjectFetcher(tt.getFetcher, fakeProto) + t.Cleanup(func() { unregisterObjectFetcher(fakeProto) }) + } + + db := &types.DatabaseV3{} + db.SetName("dummy") + db.Spec.Protocol = fakeProto + + fetcher, err := GetObjectFetcher(ctx, db, ObjectFetcherConfig{}) + tt.expectFunc(t, fetcher, err) + }) + } +} + +// unregisterObjectFetcher is reverse of RegisterObjectFetcher, but only used in tests. +func unregisterObjectFetcher(names ...string) { + objectFetchersMutex.Lock() + defer objectFetchersMutex.Unlock() + for _, name := range names { + delete(objectFetchers, name) + } +} diff --git a/lib/srv/db/objects/importer.go b/lib/srv/db/objects/importer.go new file mode 100644 index 0000000000000..cc5afc6476afb --- /dev/null +++ b/lib/srv/db/objects/importer.go @@ -0,0 +1,220 @@ +// 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 objects + +import ( + "context" + "time" + + "github.com/gravitational/trace" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" + + dbobjectv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/dbobject/v1" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/retryutils" + "github.com/gravitational/teleport/lib/utils" + "github.com/gravitational/teleport/lib/utils/interval" +) + +// singleDatabaseImporter handles importing of objects from a single database. +type singleDatabaseImporter struct { + cfg Config + + database types.Database + fetcher ObjectFetcher + + objects map[string]*objWithExpiry +} + +// objWithExpiry holds an object separate to its expiry time, avoiding the need for custom equality method for db objects and making the expiry checks explicit. +type objWithExpiry struct { + obj *dbobjectv1.DatabaseObject + expiry time.Time +} + +func startDatabaseImporter(ctx context.Context, cfg Config, database types.Database) (context.CancelFunc, error) { + cfg.Log = cfg.Log.With("database", database.GetName(), "protocol", database.GetProtocol()) + + fetcher, err := GetObjectFetcher(ctx, database, ObjectFetcherConfig{ + ImportRules: cfg.ImportRules, + Auth: cfg.Auth, + CloudClients: cfg.CloudClients, + Log: cfg.Log, + }) + if err != nil { + return nil, trace.Wrap(err) + } + + cancelCtx, cancel := context.WithCancel(ctx) + imp := newSingleDatabaseImporter(cfg, database, fetcher) + go imp.start(cancelCtx) + cfg.Log.InfoContext(ctx, "Successfully started database importer.") + return cancel, nil +} + +func newSingleDatabaseImporter(cfg Config, database types.Database, fetcher ObjectFetcher) *singleDatabaseImporter { + return &singleDatabaseImporter{ + cfg: cfg, + database: database, + fetcher: fetcher, + objects: make(map[string]*objWithExpiry), + } +} + +func (i *singleDatabaseImporter) start(ctx context.Context) { + i.cfg.Log.DebugContext(ctx, "Starting database importer.") + ticker := interval.New(interval.Config{ + Jitter: retryutils.NewSeventhJitter(), + Duration: i.cfg.ScanInterval * 7 / 6, + FirstDuration: retryutils.NewFullJitter()(i.cfg.ScanInterval), + }) + defer ticker.Stop() + + for { + select { + case <-ticker.Next(): + i.scan(ctx) + case <-ctx.Done(): + i.cfg.Log.DebugContext(ctx, "Shutting down database importer.") + return + } + } +} + +func (i *singleDatabaseImporter) scan(ctx context.Context) { + start := i.cfg.Clock.Now() + i.cfg.Log.DebugContext(ctx, "Scanning database objects.") + objectsNew, err := i.fetchObjects(ctx) + if err != nil { + i.cfg.Log.ErrorContext(ctx, "Error fetching objects", "error", err) + return + } + + objectsNewMap := utils.FromSlice(objectsNew, func(object *dbobjectv1.DatabaseObject) string { + return object.GetMetadata().Name + }) + + i.deleteObjects(ctx, calculateDeleted(ctx, i.cfg, i.objects, objectsNewMap)) + i.updateObjects(ctx, calculateUpdates(ctx, i.cfg, i.objects, objectsNewMap)) + elapsed := i.cfg.Clock.Since(start) + i.cfg.Log.DebugContext(ctx, "Scanning done.", "elapsed", elapsed) +} + +func calculateDeleted(ctx context.Context, cfg Config, objects map[string]*objWithExpiry, objsNew map[string]*dbobjectv1.DatabaseObject) []string { + var deleted []string + for key := range objects { + _, found := objsNew[key] + if !found { + deleted = append(deleted, key) + } + } + + cfg.Log.DebugContext(ctx, "Objects to delete", "count", len(deleted)) + return deleted +} + +// deleteObjects function deletes specified keys from backend and internal state. +func (i *singleDatabaseImporter) deleteObjects(ctx context.Context, deleted []string) { + var errs []error + for _, key := range deleted { + delete(i.objects, key) + err := i.cfg.DatabaseObjectClient.DeleteDatabaseObject(ctx, key) + if err != nil { + errs = append(errs, err) + } + } + + if len(errs) > 0 { + i.cfg.Log.ErrorContext(ctx, "Failed to delete some objects.", "error_count", len(errs), "errs", errs) + } +} + +func calculateUpdates(ctx context.Context, cfg Config, objects map[string]*objWithExpiry, objsNew map[string]*dbobjectv1.DatabaseObject) map[string]*objWithExpiry { + updated := make(map[string]*objWithExpiry) + now := cfg.Clock.Now() + expiry := now.Add(cfg.ObjectTTL) + + var countNew, countChanged, countRefresh int + + for key, objNew := range objsNew { + objOld, found := objects[key] + // completely new object + if !found { + countNew++ + updated[key] = &objWithExpiry{ + obj: objNew, + expiry: expiry, + } + continue + } + + // previously seen object, check for changes. + // we can safely use proto.Equal as these objects are free of revision etc. + if !proto.Equal(objOld.obj, objNew) { + countChanged++ + updated[key] = &objWithExpiry{ + obj: objNew, + expiry: expiry, + } + continue + } + + // do we need to refresh the object in backend? + if objOld.expiry.Sub(now) < cfg.RefreshThreshold { + countRefresh++ + updated[key] = &objWithExpiry{ + obj: objNew, + expiry: expiry, + } + } + } + + cfg.Log.DebugContext(ctx, "Objects to update", "new", countNew, "changed", countChanged, "refreshed", countRefresh) + + return updated +} + +func (i *singleDatabaseImporter) updateObjects(ctx context.Context, updated map[string]*objWithExpiry) { + // upsert changed objects + var errs []error + for key, objNew := range updated { + i.objects[key] = objNew + clone := proto.Clone(objNew.obj).(*dbobjectv1.DatabaseObject) + clone.Metadata.Expires = timestamppb.New(objNew.expiry) + _, err := i.cfg.DatabaseObjectClient.UpsertDatabaseObject(ctx, clone) + if err != nil { + errs = append(errs, err) + } + } + + if len(errs) > 0 { + i.cfg.Log.ErrorContext(ctx, "Errors occurred when updating objects", "error_count", len(errs), "errors", errs) + } +} + +func (i *singleDatabaseImporter) fetchObjects(ctx context.Context) ([]*dbobjectv1.DatabaseObject, error) { + var objs []*dbobjectv1.DatabaseObject + results, err := i.fetcher.FetchAll(ctx, func(_ string) bool { return true }) + if err != nil { + return nil, trace.Wrap(err) + } + for _, result := range results { + objs = append(objs, result.Objects...) + } + return objs, nil +} diff --git a/lib/srv/db/objects/importer_test.go b/lib/srv/db/objects/importer_test.go new file mode 100644 index 0000000000000..aeb0673d04593 --- /dev/null +++ b/lib/srv/db/objects/importer_test.go @@ -0,0 +1,194 @@ +// 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 objects + +import ( + "context" + "log/slog" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/jonboulle/clockwork" + "github.com/stretchr/testify/require" + "golang.org/x/exp/maps" + "google.golang.org/protobuf/testing/protocmp" + + dbobjectv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/dbobject/v1" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/srv/db/common/databaseobject" + "github.com/gravitational/teleport/lib/srv/db/common/databaseobjectimportrule" + "github.com/gravitational/teleport/lib/utils" +) + +func TestCalculateDeleted(t *testing.T) { + tests := []struct { + name string + objects map[string]*objWithExpiry + objsNew map[string]*dbobjectv1.DatabaseObject + expected []string + }{ + { + name: "all deleted", + objects: map[string]*objWithExpiry{"a": {}, "b": {}, "c": {}}, + objsNew: map[string]*dbobjectv1.DatabaseObject{}, + expected: []string{"a", "b", "c"}, + }, + { + name: "none deleted", + objects: map[string]*objWithExpiry{"a": {}, "b": {}, "c": {}}, + objsNew: map[string]*dbobjectv1.DatabaseObject{"a": {}, "b": {}, "c": {}}, + expected: []string{}, + }, + { + name: "some deleted", + objects: map[string]*objWithExpiry{"a": {}, "b": {}, "c": {}}, + objsNew: map[string]*dbobjectv1.DatabaseObject{"a": {}, "c": {}}, + expected: []string{"b"}, + }, + { + name: "empty input", + objects: map[string]*objWithExpiry{}, + objsNew: map[string]*dbobjectv1.DatabaseObject{}, + expected: []string{}, + }, + { + name: "new has more keys", + objects: map[string]*objWithExpiry{"a": {}, "b": {}}, + objsNew: map[string]*dbobjectv1.DatabaseObject{"a": {}, "b": {}, "c": {}}, + expected: []string{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := calculateDeleted(context.Background(), Config{Log: slog.Default()}, tt.objects, tt.objsNew) + require.ElementsMatch(t, tt.expected, result) + }) + } +} + +func TestCalculateUpdates(t *testing.T) { + clock := clockwork.NewFakeClock() + + mkObjectLabel := func(name string, label string) *dbobjectv1.DatabaseObject { + out, err := databaseobject.NewDatabaseObjectWithLabels(name, map[string]string{"custom": label}, &dbobjectv1.DatabaseObjectSpec{ + Protocol: types.DatabaseProtocolPostgreSQL, + DatabaseServiceName: "dummy", + ObjectKind: databaseobjectimportrule.ObjectKindTable, + Database: "dummy", + Schema: "public", + Name: name, + }) + + require.NoError(t, err) + return out + } + + mkObject := func(name string) *dbobjectv1.DatabaseObject { + return mkObjectLabel(name, "default") + } + + tests := []struct { + name string + objects []*objWithExpiry + objsNew []*dbobjectv1.DatabaseObject + expected []*objWithExpiry + }{ + { + name: "all new", + objects: []*objWithExpiry{}, + objsNew: []*dbobjectv1.DatabaseObject{ + mkObject("a"), mkObject("b"), mkObject("c"), + }, + expected: []*objWithExpiry{ + {obj: mkObject("a"), expiry: clock.Now().Add(time.Hour)}, + {obj: mkObject("b"), expiry: clock.Now().Add(time.Hour)}, + {obj: mkObject("c"), expiry: clock.Now().Add(time.Hour)}, + }, + }, + { + name: "none new or changed", + objects: []*objWithExpiry{ + {obj: mkObject("a"), expiry: clock.Now().Add(time.Hour)}, + {obj: mkObject("b"), expiry: clock.Now().Add(time.Hour)}, + {obj: mkObject("c"), expiry: clock.Now().Add(time.Hour)}, + }, + objsNew: []*dbobjectv1.DatabaseObject{ + mkObject("a"), mkObject("b"), mkObject("c"), + }, + expected: []*objWithExpiry{}, + }, + { + name: "some changed", + objects: []*objWithExpiry{ + {obj: mkObjectLabel("a", "old"), expiry: clock.Now().Add(time.Hour)}, + {obj: mkObject("b"), expiry: clock.Now().Add(time.Hour)}, + }, + objsNew: []*dbobjectv1.DatabaseObject{ + mkObjectLabel("a", "new"), mkObject("b"), + }, + expected: []*objWithExpiry{ + {obj: mkObjectLabel("a", "new"), expiry: clock.Now().Add(time.Hour)}, + }, + }, + { + name: "some refreshed", + objects: []*objWithExpiry{ + {obj: mkObject("a"), expiry: clock.Now().Add(30 * time.Second)}, + {obj: mkObject("b"), expiry: clock.Now().Add(time.Hour)}, + }, + objsNew: []*dbobjectv1.DatabaseObject{ + mkObject("a"), mkObject("b"), + }, + expected: []*objWithExpiry{ + {obj: mkObject("a"), expiry: clock.Now().Add(time.Hour)}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := Config{ + ObjectTTL: time.Hour, + RefreshThreshold: time.Minute, + Log: slog.With("test", tt.name), + Clock: clock, + } + + freshObjects := utils.FromSlice(tt.objsNew, func(object *dbobjectv1.DatabaseObject) string { + return object.GetMetadata().Name + }) + + initialState := utils.FromSlice(tt.objects, func(object *objWithExpiry) string { + return object.obj.GetMetadata().Name + }) + + expectedState := utils.FromSlice(tt.expected, func(object *objWithExpiry) string { + return object.obj.GetMetadata().Name + }) + + result := calculateUpdates(context.Background(), cfg, initialState, freshObjects) + + require.ElementsMatch(t, maps.Keys(expectedState), maps.Keys(result)) + for key, elem := range expectedState { + require.Equal(t, elem.expiry, result[key].expiry) + require.Empty(t, cmp.Diff(elem.obj, result[key].obj, protocmp.Transform())) + } + }) + } +} diff --git a/lib/srv/db/objects/objects.go b/lib/srv/db/objects/objects.go new file mode 100644 index 0000000000000..ae292ed13b52d --- /dev/null +++ b/lib/srv/db/objects/objects.go @@ -0,0 +1,214 @@ +// 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 objects + +import ( + "context" + "log/slog" + "os" + "sync" + "time" + + "github.com/gravitational/trace" + "github.com/jonboulle/clockwork" + + "github.com/gravitational/teleport" + "github.com/gravitational/teleport/api/client/databaseobject" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/cloud" + "github.com/gravitational/teleport/lib/srv/db/common" +) + +type Objects interface { + StartImporter(ctx context.Context, database types.Database) error + StopImporter(databaseName string) error +} + +type Config struct { + DatabaseObjectClient *databaseobject.Client + ImportRules ImportRulesReader + Auth common.Auth + CloudClients cloud.Clients + + // ScanInterval specifies how often the database is scanned. + // A higher ScanInterval reduces the load on the database and database agent, + // but increases the delay in detecting schema changes or updates from new import rules. + ScanInterval time.Duration + + // ObjectTTL defines TTL for a newly created object. + // A higher ObjectTTL reduces the backend load and should be significantly larger than ScanInterval. + // Setting a TTL for database objects ensures cleanup if the database becomes unavailable or the database agent stops performing scans. + ObjectTTL time.Duration + + // RefreshThreshold sets the minimum remaining TTL for an object to qualify for a TTL refresh. + RefreshThreshold time.Duration + + Clock clockwork.Clock + Log *slog.Logger +} + +// loadEnvVar parses the named env vars as a duration. +func (c *Config) loadEnvVar(ctx context.Context, name string) (bool, time.Duration) { + envVar := os.Getenv(name) + if envVar == "" { + return false, 0 + } + if envVar == "never" { + return true, 0 + } + + interval, err := time.ParseDuration(envVar) + if err != nil { + c.Log.ErrorContext(ctx, "Failed to parse env var, override not applied.", "name", name, "value", envVar) + } + + return true, interval +} + +func (c *Config) loadEnvVarOverrides(ctx context.Context) { + needInfo := false + // overriding scan interval modifies the other variables, but not the other way around. + if found, value := c.loadEnvVar(ctx, "TELEPORT_UNSTABLE_DB_OBJECTS_SCAN_INTERVAL"); found { + // multipliers 12 and 3 mimic the interval length proportions of default configuration (15/180/45 minutes). + c.ScanInterval = value + c.ObjectTTL = value * 12 + c.RefreshThreshold = value * 3 + needInfo = true + } + if found, value := c.loadEnvVar(ctx, "TELEPORT_UNSTABLE_DB_OBJECTS_OBJECT_TTL"); found { + c.ObjectTTL = value + needInfo = true + } + if found, value := c.loadEnvVar(ctx, "TELEPORT_UNSTABLE_DB_OBJECTS_REFRESH_THRESHOLD"); found { + c.RefreshThreshold = value + needInfo = true + } + + if needInfo { + c.Log.InfoContext(ctx, "Applied env var overrides.", "scan_interval", c.ScanInterval, "object_ttl", c.ObjectTTL, "refresh_threshold", c.RefreshThreshold) + } +} + +func (c *Config) CheckAndSetDefaults(ctx context.Context) error { + if c.DatabaseObjectClient == nil { + return trace.BadParameter("missing parameter DatabaseObjectClient") + } + if c.ImportRules == nil { + return trace.BadParameter("missing parameter ImportRules") + } + if c.Auth == nil { + return trace.BadParameter("missing parameter Auth") + } + if c.CloudClients == nil { + return trace.BadParameter("missing parameter CloudClients") + } + if c.Log == nil { + c.Log = slog.Default().With(teleport.ComponentKey, "db:obj_importer") + } + if c.Clock == nil { + c.Clock = clockwork.NewRealClock() + } + if c.ScanInterval == 0 { + c.ScanInterval = time.Minute * 15 + } + if c.ObjectTTL == 0 { + c.ObjectTTL = time.Minute * 180 + } + if c.RefreshThreshold == 0 { + c.RefreshThreshold = time.Minute * 45 + } + + c.loadEnvVarOverrides(ctx) + + return nil +} + +func (c *Config) disabled() bool { + return c.ObjectTTL <= 0 || c.ScanInterval <= 0 +} + +type objects struct { + cfg Config + + importerMap map[string]context.CancelFunc + importersMutex sync.RWMutex +} + +func NewObjects(ctx context.Context, cfg Config) (Objects, error) { + err := cfg.CheckAndSetDefaults(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + result := &objects{ + cfg: cfg, + importerMap: make(map[string]context.CancelFunc), + } + if result.disabled() { + cfg.Log.WarnContext(ctx, "Objects importer is disabled through config.") + } + return result, nil +} + +var _ Objects = (*objects)(nil) + +// StartImporter starts a new importer for a given database. +// An error will be returned only in case of interface misuse, e.g. attempt to start the importer for same database twice. +// If the database configuration (protocol/type/parameters) is not supported, no error will be returned. +func (o *objects) StartImporter(ctx context.Context, database types.Database) error { + if o.disabled() { + return nil + } + + o.importersMutex.Lock() + defer o.importersMutex.Unlock() + + if _, ok := o.importerMap[database.GetName()]; ok { + return trace.AlreadyExists("importer for database %q already started", database.GetName()) + } + stopImporterFunc, err := startDatabaseImporter(ctx, o.cfg, database) + if err != nil { + // register dummy "stop" function to avoid errors on shutdown. + o.importerMap[database.GetName()] = func() {} + return trace.Wrap(err) + } + o.importerMap[database.GetName()] = stopImporterFunc + return nil +} + +// StopImporter stops the running importer for a given database. +func (o *objects) StopImporter(name string) error { + if o.disabled() { + return nil + } + + o.importersMutex.Lock() + defer o.importersMutex.Unlock() + + stopImporterFunc, ok := o.importerMap[name] + if !ok { + return trace.NotFound("no importer found for database %q", name) + } + if stopImporterFunc != nil { + stopImporterFunc() + } + delete(o.importerMap, name) + return nil +} + +func (o *objects) disabled() bool { + return o.cfg.disabled() +} diff --git a/lib/srv/db/objects/objects_test.go b/lib/srv/db/objects/objects_test.go new file mode 100644 index 0000000000000..83408b78ae6d2 --- /dev/null +++ b/lib/srv/db/objects/objects_test.go @@ -0,0 +1,164 @@ +// 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 objects + +import ( + "context" + "log/slog" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestLoadEnvVar(t *testing.T) { + testCases := []struct { + name string + envValue string + expectedFound bool + expectedDuration time.Duration + }{ + {"empty", "", false, 0}, + {"never", "never", true, 0}, + {"valid duration", "1h", true, time.Hour}, + {"invalid duration", "invalid", true, 0}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Setenv("TEST_ENV_VAR", tc.envValue) + c := &Config{Log: slog.Default()} + + found, duration := c.loadEnvVar(context.Background(), "TEST_ENV_VAR") + + require.Equal(t, tc.expectedFound, found) + require.Equal(t, tc.expectedDuration, duration) + }) + } +} + +func TestLoadEnvVarOverrides(t *testing.T) { + testCases := []struct { + name string + envVars map[string]string + expectedScanInt time.Duration + expectedObjectTTL time.Duration + expectedRefreshTh time.Duration + }{ + { + name: "no overrides", + envVars: map[string]string{}, + expectedScanInt: 0, + expectedObjectTTL: 0, + expectedRefreshTh: 0, + }, + { + name: "scan interval only", + envVars: map[string]string{ + "TELEPORT_UNSTABLE_DB_OBJECTS_SCAN_INTERVAL": "1h", + }, + expectedScanInt: time.Hour, + expectedObjectTTL: 12 * time.Hour, + expectedRefreshTh: 3 * time.Hour, + }, + { + name: "object ttl only", + envVars: map[string]string{ + "TELEPORT_UNSTABLE_DB_OBJECTS_OBJECT_TTL": "2h", + }, + expectedScanInt: 0, + expectedObjectTTL: 2 * time.Hour, + expectedRefreshTh: 0, + }, + { + name: "refresh threshold only", + envVars: map[string]string{ + "TELEPORT_UNSTABLE_DB_OBJECTS_REFRESH_THRESHOLD": "3h", + }, + expectedScanInt: 0, + expectedObjectTTL: 0, + expectedRefreshTh: 3 * time.Hour, + }, + { + name: "all overrides", + envVars: map[string]string{ + "TELEPORT_UNSTABLE_DB_OBJECTS_SCAN_INTERVAL": "1h", + "TELEPORT_UNSTABLE_DB_OBJECTS_OBJECT_TTL": "2h", + "TELEPORT_UNSTABLE_DB_OBJECTS_REFRESH_THRESHOLD": "3h", + }, + expectedScanInt: time.Hour, + expectedObjectTTL: 2 * time.Hour, + expectedRefreshTh: 3 * time.Hour, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + for k, v := range tc.envVars { + t.Setenv(k, v) + } + + c := &Config{Log: slog.Default()} + c.loadEnvVarOverrides(context.Background()) + + require.Equal(t, tc.expectedScanInt, c.ScanInterval) + require.Equal(t, tc.expectedObjectTTL, c.ObjectTTL) + require.Equal(t, tc.expectedRefreshTh, c.RefreshThreshold) + }) + } +} + +func TestConfigDisabled(t *testing.T) { + testCases := []struct { + name string + cfg *Config + expected bool + }{ + { + name: "default config", + cfg: &Config{ + ScanInterval: time.Minute * 15, + ObjectTTL: time.Minute * 180, + RefreshThreshold: time.Minute * 45, + }, + expected: false, + }, + { + name: "disabled through object TTL", + cfg: &Config{ + ScanInterval: time.Minute * 15, + ObjectTTL: 0, + RefreshThreshold: time.Minute * 45, + }, + expected: true, + }, + { + name: "disabled through scan interval", + cfg: &Config{ + ScanInterval: 0, + ObjectTTL: time.Minute * 180, + RefreshThreshold: time.Minute * 45, + }, + expected: true, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + require.Equal(t, tc.expected, tc.cfg.disabled()) + }) + } +} diff --git a/lib/srv/db/postgres/connector.go b/lib/srv/db/postgres/connector.go new file mode 100644 index 0000000000000..08bdae1fb3a0c --- /dev/null +++ b/lib/srv/db/postgres/connector.go @@ -0,0 +1,157 @@ +// 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 postgres + +import ( + "context" + "fmt" + "log/slog" + "time" + + "github.com/gravitational/trace" + "github.com/jackc/pgconn" + "github.com/jackc/pgx/v4" + + "github.com/gravitational/teleport/api/types" + libcloud "github.com/gravitational/teleport/lib/cloud" + "github.com/gravitational/teleport/lib/srv/db/cloud" + "github.com/gravitational/teleport/lib/srv/db/common" + discoverycommon "github.com/gravitational/teleport/lib/srv/discovery/common" +) + +type connector struct { + auth common.Auth + cloudClients libcloud.Clients + log *slog.Logger + + certExpiry time.Time + database types.Database + databaseUser string + databaseName string + startupParams map[string]string +} + +func (c *connector) getConnectConfig(ctx context.Context) (*pgconn.Config, error) { + // The driver requires the config to be built by parsing the connection + // string so parse the basic template and then fill in the rest of + // parameters such as TLS configuration. + config, err := pgconn.ParseConfig(fmt.Sprintf("postgres://%s", c.database.GetURI())) + if err != nil { + return nil, trace.Wrap(err) + } + // TLS config will use client certificate for an onprem database or + // will contain RDS root certificate for RDS/Aurora. + config.TLSConfig, err = c.auth.GetTLSConfig(ctx, c.certExpiry, c.database, c.databaseUser) + if err != nil { + return nil, trace.Wrap(err) + } + config.User = c.databaseUser + config.Database = c.databaseName + // Pgconn adds fallbacks to retry connection without TLS if the TLS + // attempt fails. Reset the fallbacks to avoid retries, otherwise + // it's impossible to debug TLS connection errors. + config.Fallbacks = nil + // Set startup parameters that the client sent us. + config.RuntimeParams = c.startupParams + // AWS RDS/Aurora and GCP Cloud SQL use IAM authentication so request an + // auth token and use it as a password. + switch c.database.GetType() { + case types.DatabaseTypeRDS, types.DatabaseTypeRDSProxy: + config.Password, err = c.auth.GetRDSAuthToken(ctx, c.database, c.databaseUser) + if err != nil { + return nil, trace.Wrap(err) + } + case types.DatabaseTypeRedshift: + config.User, config.Password, err = c.auth.GetRedshiftAuthToken(ctx, c.database, c.databaseUser, c.databaseName) + if err != nil { + return nil, trace.Wrap(err) + } + case types.DatabaseTypeRedshiftServerless: + config.User, config.Password, err = c.auth.GetRedshiftServerlessAuthToken(ctx, c.database, c.databaseUser, c.databaseName) + if err != nil { + return nil, trace.Wrap(err) + } + case types.DatabaseTypeCloudSQL: + config.Password, err = c.auth.GetCloudSQLAuthToken(ctx, c.databaseUser) + if err != nil { + return nil, trace.Wrap(err) + } + // Get the client once for subsequent calls (it acquires a read lock). + gcpClient, err := c.cloudClients.GetGCPSQLAdminClient(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + // Detect whether the instance is set to require SSL. + // Fallback to not requiring SSL for access denied errors. + requireSSL, err := cloud.GetGCPRequireSSL(ctx, c.database, gcpClient) + if err != nil && !trace.IsAccessDenied(err) { + return nil, trace.Wrap(err) + } + // Create ephemeral certificate and append to TLS config when + // the instance requires SSL. + if requireSSL { + err = cloud.AppendGCPClientCert(ctx, c.certExpiry, c.database, gcpClient, config.TLSConfig) + if err != nil { + return nil, trace.Wrap(err) + } + } + case types.DatabaseTypeAzure: + config.Password, err = c.auth.GetAzureAccessToken(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + config.User = discoverycommon.MakeAzureDatabaseLoginUsername(c.database, config.User) + } + return config, nil +} + +// pgxConnect connects to the database using pgx driver which is higher-level +// than pgconn and is easier to use for executing queries. +func (c *connector) pgxConnect(ctx context.Context) (*pgx.Conn, error) { + config, err := c.getConnectConfig(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + pgxConf, err := pgx.ParseConfig("") + if err != nil { + return nil, trace.Wrap(err) + } + pgxConf.Config = *config + c.log.DebugContext(ctx, "Connecting to database", "db_name", config.Database, "db_user", config.User, "host", config.Host) + return pgx.ConnectConfig(ctx, pgxConf) +} + +// withDefaultDatabase returns a copy of connector with databaseName switched to the default database, if one is available. +func (c *connector) withDefaultDatabase() *connector { + copied := *c + if c.database.GetAdminUser().DefaultDatabase != "" { + copied.databaseName = c.database.GetAdminUser().DefaultDatabase + } + return &copied +} + +// connectAsAdmin connect to the database from db route as admin user. +// If useDefaultDatabase is true and a default database is configured for the admin user, it will be used instead. +func (c *connector) connectAsAdmin(ctx context.Context) (*pgx.Conn, error) { + // make a copy to override the database user as well as to clear potential startup params. + copied := *c + copied.databaseUser = c.database.GetAdminUser().Name + copied.startupParams = make(map[string]string) + + conn, err := copied.pgxConnect(ctx) + return conn, trace.Wrap(err) +} diff --git a/lib/srv/db/postgres/engine.go b/lib/srv/db/postgres/engine.go index 061b7dc9ad3bb..af5e035635c76 100644 --- a/lib/srv/db/postgres/engine.go +++ b/lib/srv/db/postgres/engine.go @@ -33,12 +33,9 @@ import ( "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/defaults" - "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/types/events" - "github.com/gravitational/teleport/lib/srv/db/cloud" "github.com/gravitational/teleport/lib/srv/db/common" "github.com/gravitational/teleport/lib/srv/db/common/role" - discoverycommon "github.com/gravitational/teleport/lib/srv/discovery/common" "github.com/gravitational/teleport/lib/utils" logutil "github.com/gravitational/teleport/lib/utils/log" ) @@ -256,7 +253,7 @@ func (e *Engine) checkAccess(ctx context.Context, sessionCtx *common.Session) er // the hijacked connection and the frontend, an interface used for message // exchange with the database. func (e *Engine) connect(ctx context.Context, sessionCtx *common.Session) (*pgproto3.Frontend, *pgconn.HijackedConn, error) { - connectConfig, err := e.getConnectConfig(ctx, sessionCtx) + connectConfig, err := e.newConnector(sessionCtx).getConnectConfig(ctx) if err != nil { return nil, nil, trace.Wrap(err) } @@ -490,80 +487,19 @@ func (e *Engine) receiveFromServer(serverConn *pgconn.PgConn, serverErrCh chan<- log.DebugContext(e.Context, "Stopped receiving from server.", "total_bytes", total) } -// getConnectConfig returns config that can be used to connect to the -// database instance. -func (e *Engine) getConnectConfig(ctx context.Context, sessionCtx *common.Session) (*pgconn.Config, error) { - // The driver requires the config to be built by parsing the connection - // string so parse the basic template and then fill in the rest of - // parameters such as TLS configuration. - config, err := pgconn.ParseConfig(fmt.Sprintf("postgres://%s", sessionCtx.Database.GetURI())) - if err != nil { - return nil, trace.Wrap(err) - } - // TLS config will use client certificate for an onprem database or - // will contain RDS root certificate for RDS/Aurora. - config.TLSConfig, err = e.Auth.GetTLSConfig(ctx, sessionCtx.GetExpiry(), sessionCtx.Database, sessionCtx.DatabaseUser) - if err != nil { - return nil, trace.Wrap(err) - } - config.User = sessionCtx.DatabaseUser - config.Database = sessionCtx.DatabaseName - // Pgconn adds fallbacks to retry connection without TLS if the TLS - // attempt fails. Reset the fallbacks to avoid retries, otherwise - // it's impossible to debug TLS connection errors. - config.Fallbacks = nil - // Set startup parameters that the client sent us. - config.RuntimeParams = sessionCtx.StartupParameters - // AWS RDS/Aurora and GCP Cloud SQL use IAM authentication so request an - // auth token and use it as a password. - switch sessionCtx.Database.GetType() { - case types.DatabaseTypeRDS, types.DatabaseTypeRDSProxy: - config.Password, err = e.Auth.GetRDSAuthToken(ctx, sessionCtx.Database, sessionCtx.DatabaseUser) - if err != nil { - return nil, trace.Wrap(err) - } - case types.DatabaseTypeRedshift: - config.User, config.Password, err = e.Auth.GetRedshiftAuthToken(ctx, sessionCtx.Database, sessionCtx.DatabaseUser, sessionCtx.DatabaseName) - if err != nil { - return nil, trace.Wrap(err) - } - case types.DatabaseTypeRedshiftServerless: - config.User, config.Password, err = e.Auth.GetRedshiftServerlessAuthToken(ctx, sessionCtx.Database, sessionCtx.DatabaseUser, sessionCtx.DatabaseName) - if err != nil { - return nil, trace.Wrap(err) - } - case types.DatabaseTypeCloudSQL: - config.Password, err = e.Auth.GetCloudSQLAuthToken(ctx, sessionCtx.DatabaseUser) - if err != nil { - return nil, trace.Wrap(err) - } - // Get the client once for subsequent calls (it acquires a read lock). - gcpClient, err := e.CloudClients.GetGCPSQLAdminClient(ctx) - if err != nil { - return nil, trace.Wrap(err) - } - // Detect whether the instance is set to require SSL. - // Fallback to not requiring SSL for access denied errors. - requireSSL, err := cloud.GetGCPRequireSSL(ctx, sessionCtx, gcpClient) - if err != nil && !trace.IsAccessDenied(err) { - return nil, trace.Wrap(err) - } - // Create ephemeral certificate and append to TLS config when - // the instance requires SSL. - if requireSSL { - err = cloud.AppendGCPClientCert(ctx, sessionCtx, gcpClient, config.TLSConfig) - if err != nil { - return nil, trace.Wrap(err) - } - } - case types.DatabaseTypeAzure: - config.Password, err = e.Auth.GetAzureAccessToken(ctx) - if err != nil { - return nil, trace.Wrap(err) - } - config.User = discoverycommon.MakeAzureDatabaseLoginUsername(sessionCtx.Database, config.User) +func (e *Engine) newConnector(sessionCtx *common.Session) *connector { + conn := &connector{ + auth: e.Auth, + cloudClients: e.CloudClients, + log: e.Log, + + certExpiry: sessionCtx.GetExpiry(), + database: sessionCtx.Database, + databaseUser: sessionCtx.DatabaseUser, + databaseName: sessionCtx.DatabaseName, + startupParams: sessionCtx.StartupParameters, } - return config, nil + return conn } // handleCancelRequest handles a cancel request and returns immediately (closing the connection). diff --git a/lib/srv/db/postgres/objects.go b/lib/srv/db/postgres/objects.go new file mode 100644 index 0000000000000..f965baf5cf05c --- /dev/null +++ b/lib/srv/db/postgres/objects.go @@ -0,0 +1,134 @@ +// 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 postgres + +import ( + "context" + "time" + + "github.com/gravitational/trace" + "github.com/jackc/pgx/v4" + + dbobjectv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/dbobject/v1" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/srv/db/common/databaseobjectimportrule" + "github.com/gravitational/teleport/lib/srv/db/objects" +) + +// TODO(Tener): add e2e tests for objectFetcher. +type objectFetcher struct { + cfg objects.ObjectFetcherConfig + db types.Database +} + +var _ objects.ObjectFetcher = (*objectFetcher)(nil) + +func NewObjectFetcher(ctx context.Context, db types.Database, cfg objects.ObjectFetcherConfig) (objects.ObjectFetcher, error) { + if db.GetAdminUser().Name == "" { + return nil, objects.NewErrFetcherDisabled("no admin user configured") + } + return &objectFetcher{cfg: cfg, db: db}, nil +} + +func (f *objectFetcher) FetchAll(ctx context.Context, dbNameFilter databaseobjectimportrule.DbNameFilter) (map[string]objects.FetchResult, error) { + names, err := f.getDatabaseNames(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + result := make(map[string]objects.FetchResult) + + for _, dbName := range names { + if dbNameFilter(dbName) == false { + continue + } + objs, fetchErr := f.FetchOneDatabase(ctx, dbName) + result[dbName] = objects.FetchResult{ + Objects: objs, + Error: fetchErr, + } + } + + return result, nil +} + +func (f *objectFetcher) FetchOneDatabase(ctx context.Context, databaseName string) ([]*dbobjectv1.DatabaseObject, error) { + conn, err := f.connectAsAdmin(ctx, databaseName) + if err != nil { + return nil, trace.Wrap(err) + } + defer conn.Close(ctx) + + objsFetched, err := fetchDatabaseObjects(ctx, f.db, databaseName, conn) + if err != nil { + return nil, trace.Wrap(err) + } + f.cfg.Log.InfoContext(ctx, "fetched objects from database", "count", len(objsFetched)) + + return objsFetched, nil +} + +func (f *objectFetcher) getDatabaseNames(ctx context.Context) ([]string, error) { + dbName := f.db.GetAdminUser().DefaultDatabase + if dbName == "" { + dbName = "postgres" + f.cfg.Log.WarnContext(ctx, "No default database configured, using default.", "db_name", dbName) + } + conn, err := f.connectAsAdmin(ctx, dbName) + if err != nil { + return nil, trace.Wrap(err) + } + defer conn.Close(ctx) + + rows, err := conn.Query(context.Background(), "SELECT pg_database.datname FROM pg_catalog.pg_database WHERE pg_database.datistemplate = false;") + if err != nil { + return nil, trace.Wrap(err) + } + defer rows.Close() + + var databases []string + for rows.Next() { + var datname string + err := rows.Scan(&datname) + if err != nil { + return nil, trace.Wrap(err) + } + databases = append(databases, datname) + } + + if rows.Err() != nil { + return nil, trace.Wrap(rows.Err()) + } + + return databases, nil +} + +func (f *objectFetcher) connectAsAdmin(ctx context.Context, databaseName string) (*pgx.Conn, error) { + conn := &connector{ + auth: f.cfg.Auth, + cloudClients: f.cfg.CloudClients, + log: f.cfg.Log, + + certExpiry: time.Now().Add(time.Hour), + database: f.db, + databaseUser: f.db.GetAdminUser().Name, + databaseName: databaseName, + + startupParams: map[string]string{}, + } + return conn.connectAsAdmin(ctx) +} diff --git a/lib/srv/db/postgres/schema.go b/lib/srv/db/postgres/schema.go index 4712d764f923a..8dfe0a8f9407d 100644 --- a/lib/srv/db/postgres/schema.go +++ b/lib/srv/db/postgres/schema.go @@ -22,7 +22,7 @@ import ( "github.com/jackc/pgx/v4" dbobjectv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/dbobject/v1" - "github.com/gravitational/teleport/lib/srv/db/common" + "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/srv/db/common/databaseobject" "github.com/gravitational/teleport/lib/srv/db/common/databaseobjectimportrule" ) @@ -36,11 +36,9 @@ type schema struct { } // schemaInfoQuery is a query to return the schema info. -// TODO(Tener): for very large schemas adding a filtering right into this query could improve performance. -// It doesn't appear necessary right now. const schemaInfoQuery = "SELECT schemaname, tablename FROM pg_catalog.pg_tables" -func fetchDatabaseObjects(ctx context.Context, session *common.Session, conn *pgx.Conn) ([]*dbobjectv1.DatabaseObject, error) { +func fetchDatabaseObjects(ctx context.Context, database types.Database, databaseName string, conn *pgx.Conn) ([]*dbobjectv1.DatabaseObject, error) { s, err := getSchemaInfo(ctx, conn) if err != nil { return nil, trace.Wrap(err) @@ -51,20 +49,20 @@ func fetchDatabaseObjects(ctx context.Context, session *common.Session, conn *pg for schemaName, schemaVal := range s { for _, table := range schemaVal.tables { name := strings.Join([]string{ - session.Database.GetProtocol(), - session.Database.GetType(), - session.Database.GetName(), + database.GetProtocol(), + database.GetType(), + database.GetName(), databaseobjectimportrule.ObjectKindTable, - session.DatabaseName, + databaseName, schemaName, table, - }, "/") + }, "::") obj, err := databaseobject.NewDatabaseObject(name, &dbobjectv1.DatabaseObjectSpec{ ObjectKind: databaseobjectimportrule.ObjectKindTable, - DatabaseServiceName: session.Database.GetName(), - Protocol: session.Database.GetProtocol(), - Database: session.DatabaseName, + DatabaseServiceName: database.GetName(), + Protocol: database.GetProtocol(), + Database: databaseName, Schema: schemaName, Name: table, }) diff --git a/lib/srv/db/postgres/users.go b/lib/srv/db/postgres/users.go index 355e384f4c695..4b6a4ee0e7c27 100644 --- a/lib/srv/db/postgres/users.go +++ b/lib/srv/db/postgres/users.go @@ -40,19 +40,17 @@ import ( "github.com/gravitational/teleport/lib/srv/db/common" "github.com/gravitational/teleport/lib/srv/db/common/databaseobjectimportrule" "github.com/gravitational/teleport/lib/srv/db/common/permissions" + "github.com/gravitational/teleport/lib/srv/db/objects" ) -// connectAsAdmin connect to the database from db route as admin user. -// If useDefaultDatabase is true and a default database is configured for the admin user, it will be used instead. -func (e *Engine) connectAsAdmin(ctx context.Context, sessionCtx *common.Session, useDefaultDatabase bool) (*pgx.Conn, error) { - loginDatabase := sessionCtx.DatabaseName - if useDefaultDatabase && sessionCtx.Database.GetAdminUser().DefaultDatabase != "" { - loginDatabase = sessionCtx.Database.GetAdminUser().DefaultDatabase - } else { - e.Log.InfoContext(ctx, "Connecting to session database", "database", loginDatabase) - } - conn, err := e.pgxConnect(ctx, sessionCtx.WithUserAndDatabase(sessionCtx.Database.GetAdminUser().Name, loginDatabase)) - return conn, trace.Wrap(err) +// connectAsAdmin connects as the admin user to the default database, per database settings, or as a fallback to the one specified in sessionCtx. +func (e *Engine) connectAsAdminDefaultDatabase(ctx context.Context, sessionCtx *common.Session) (*pgx.Conn, error) { + return e.newConnector(sessionCtx).withDefaultDatabase().connectAsAdmin(ctx) +} + +// connectAsAdmin connects as the admin user to the database specified in sessionCtx. +func (e *Engine) connectAsAdminSessionDatabase(ctx context.Context, sessionCtx *common.Session) (*pgx.Conn, error) { + return e.newConnector(sessionCtx).connectAsAdmin(ctx) } // ActivateUser creates or enables the database user. @@ -66,7 +64,7 @@ func (e *Engine) ActivateUser(ctx context.Context, sessionCtx *common.Session) e return trace.BadParameter("auto-user provisioning is not supported for RDS reader endpoints") } - conn, err := e.connectAsAdmin(ctx, sessionCtx, true) + conn, err := e.connectAsAdminDefaultDatabase(ctx, sessionCtx) if err != nil { return trace.Wrap(err) } @@ -201,33 +199,25 @@ func (e *Engine) applyPermissions(ctx context.Context, sessionCtx *common.Sessio return trace.BadParameter("fine-grained database permissions and database roles are mutually exclusive, yet both were provided.") } - rules, err := e.AuthClient.GetDatabaseObjectImportRules(ctx) - if err != nil { - return trace.Wrap(err) - } - - conn, err := e.connectAsAdmin(ctx, sessionCtx, false) + fetcher, err := objects.GetObjectFetcher(ctx, sessionCtx.Database, objects.ObjectFetcherConfig{ + ImportRules: e.AuthClient, + Auth: e.Auth, + CloudClients: e.CloudClients, + Log: e.Log, + }) if err != nil { - e.Log.ErrorContext(e.Context, "Failed to connect to the database.", "error", err) return trace.Wrap(err) } - defer conn.Close(ctx) - - objsFetched, err := fetchDatabaseObjects(ctx, sessionCtx, conn) + objsImported, err := fetcher.FetchOneDatabase(ctx, sessionCtx.DatabaseName) if err != nil { return trace.Wrap(err) } - counts, _ := permissions.CountObjectKinds(objsFetched) - e.Log.InfoContext(ctx, "Database objects fetched from the database.", "counts", counts, "total", len(objsFetched)) - - objsImported, errCount := databaseobjectimportrule.ApplyDatabaseObjectImportRules(ctx, e.Log, rules, sessionCtx.Database, objsFetched) - counts, _ = permissions.CountObjectKinds(objsImported) - e.Log.InfoContext(ctx, "Database objects imported.", "counts", counts, "err_count", errCount, "total", len(objsFetched)) permissionSet, err := permissions.CalculatePermissions(sessionCtx.Checker, sessionCtx.Database, objsImported) if err != nil { return trace.Wrap(err) } + summary, eventData := permissions.SummarizePermissions(permissionSet) e.Log.InfoContext(ctx, "Calculated database permissions.", "summary", summary, "user", sessionCtx.DatabaseUser) e.auditUserPermissions(sessionCtx, eventData) @@ -237,6 +227,15 @@ func (e *Engine) applyPermissions(ctx context.Context, sessionCtx *common.Sessio return trace.Wrap(err) } + conn, err := e.connectAsAdminSessionDatabase(ctx, sessionCtx) + if err != nil { + e.Log.ErrorContext(ctx, "Failed to connect to the database.", "error", err) + return trace.Wrap(err) + } + defer conn.Close(ctx) + + // teleport_remove_permissions and teleport_update_permissions are created in pg_temp table of the session database. + // teleport_remove_permissions gets called by teleport_update_permissions as needed. if err := e.createProcedures(ctx, sessionCtx, conn, []string{removePermissionsProcName, updatePermissionsProcName}); err != nil { return trace.Wrap(err) } @@ -263,7 +262,7 @@ func (e *Engine) removePermissions(ctx context.Context, sessionCtx *common.Sessi } logger.InfoContext(ctx, "Removing permissions from PostgreSQL user.") - conn, err := e.connectAsAdmin(ctx, sessionCtx, false) + conn, err := e.connectAsAdminSessionDatabase(ctx, sessionCtx) if err != nil { return trace.Wrap(err) } @@ -290,7 +289,7 @@ func (e *Engine) DeactivateUser(ctx context.Context, sessionCtx *common.Session) // removal may yield errors, but we will still attempt to deactivate the user. errRemove := trace.Wrap(e.removePermissions(ctx, sessionCtx)) - conn, err := e.connectAsAdmin(ctx, sessionCtx, true) + conn, err := e.connectAsAdminDefaultDatabase(ctx, sessionCtx) if err != nil { return trace.NewAggregate(errRemove, trace.Wrap(err)) } @@ -323,7 +322,7 @@ func (e *Engine) DeleteUser(ctx context.Context, sessionCtx *common.Session) err // removal may yield errors, but we will still attempt to delete the user. errRemove := trace.Wrap(e.removePermissions(ctx, sessionCtx)) - conn, err := e.connectAsAdmin(ctx, sessionCtx, true) + conn, err := e.connectAsAdminDefaultDatabase(ctx, sessionCtx) if err != nil { return trace.NewAggregate(errRemove, trace.Wrap(err)) } @@ -408,21 +407,6 @@ func (e *Engine) updateAutoUsersRole(ctx context.Context, conn *pgx.Conn) error return nil } -// pgxConnect connects to the database using pgx driver which is higher-level -// than pgconn and is easier to use for executing queries. -func (e *Engine) pgxConnect(ctx context.Context, sessionCtx *common.Session) (*pgx.Conn, error) { - config, err := e.getConnectConfig(ctx, sessionCtx) - if err != nil { - return nil, trace.Wrap(err) - } - pgxConf, err := pgx.ParseConfig("") - if err != nil { - return nil, trace.Wrap(err) - } - pgxConf.Config = *config - return pgx.ConnectConfig(ctx, pgxConf) -} - // callProcedure calls the procedure with the provided arguments. func (e *Engine) callProcedure(ctx context.Context, sessionCtx *common.Session, conn *pgx.Conn, procName string, args ...any) error { query, err := buildCallQuery(sessionCtx, procName) diff --git a/lib/srv/db/server.go b/lib/srv/db/server.go index a332e9bd8266c..799d279fb487c 100644 --- a/lib/srv/db/server.go +++ b/lib/srv/db/server.go @@ -58,6 +58,7 @@ import ( "github.com/gravitational/teleport/lib/srv/db/elasticsearch" "github.com/gravitational/teleport/lib/srv/db/mongodb" "github.com/gravitational/teleport/lib/srv/db/mysql" + "github.com/gravitational/teleport/lib/srv/db/objects" "github.com/gravitational/teleport/lib/srv/db/opensearch" "github.com/gravitational/teleport/lib/srv/db/postgres" "github.com/gravitational/teleport/lib/srv/db/redis" @@ -82,6 +83,8 @@ func init() { common.RegisterEngine(clickhouse.NewEngine, defaults.ProtocolClickHouse) common.RegisterEngine(clickhouse.NewEngine, defaults.ProtocolClickHouseHTTP) common.RegisterEngine(spanner.NewEngine, defaults.ProtocolSpanner) + + objects.RegisterObjectFetcher(postgres.NewObjectFetcher, defaults.ProtocolPostgres) } // Config is the configuration for a database proxy server. @@ -141,6 +144,8 @@ type Config struct { ConnectedProxyGetter *reversetunnel.ConnectedProxyGetter // CloudUsers manage users for cloud hosted databases. CloudUsers *users.Users + // DatabaseObjects manages database object importers. + DatabaseObjects objects.Objects // ConnectionMonitor monitors and closes connections if session controls // prevent the connections. ConnectionMonitor ConnMonitor @@ -258,6 +263,18 @@ func (c *Config) CheckAndSetDefaults(ctx context.Context) (err error) { } } + if c.DatabaseObjects == nil { + c.DatabaseObjects, err = objects.NewObjects(ctx, objects.Config{ + DatabaseObjectClient: c.AuthClient.DatabaseObjectsClient(), + ImportRules: c.AuthClient, + Auth: c.Auth, + CloudClients: c.CloudClients, + }) + if err != nil { + return trace.Wrap(err) + } + } + if c.discoveryResourceChecker == nil { c.discoveryResourceChecker, err = cloud.NewDiscoveryResourceChecker(cloud.DiscoveryResourceCheckerConfig{ ResourceMatchers: c.ResourceMatchers, @@ -470,6 +487,17 @@ func (s *Server) startDatabase(ctx context.Context, database types.Database) err if err := s.cfg.CloudUsers.Setup(ctx, database); err != nil { s.log.WarnContext(ctx, "Failed to setup users.", "database", database.GetName(), "error", err) } + // Start database object importer. + if err := s.cfg.DatabaseObjects.StartImporter(ctx, database); err != nil { + // special handling for "not implemented" errors; these are very likely to occur and aren't as interesting. + if trace.IsNotImplemented(err) { + s.log.DebugContext(ctx, "Database object importer not implemented.", "database", database.GetName()) + } else if match, reason := objects.IsErrFetcherDisabled(err); match { + s.log.DebugContext(ctx, "Database object importer cannot be started due to disabled fetcher", "reason", reason, "database", database.GetName()) + } else { + s.log.WarnContext(ctx, "Failed to start database object importer.", "database", database.GetName(), "error", err) + } + } s.log.DebugContext(ctx, "Started database.", "db", database) return nil @@ -477,6 +505,10 @@ func (s *Server) startDatabase(ctx context.Context, database types.Database) err // stopDatabase uninitializes the database with the specified name. func (s *Server) stopDatabase(ctx context.Context, name string) error { + // Stop database object importer. + if err := s.cfg.DatabaseObjects.StopImporter(name); err != nil { + s.log.WarnContext(ctx, "Failed to stop database object importer.", "db", name, "error", err) + } s.stopDynamicLabels(name) if err := s.stopHeartbeat(name); err != nil { return trace.Wrap(err) diff --git a/lib/srv/discovery/access_graph.go b/lib/srv/discovery/access_graph.go index 3eec9262a16d1..fc246d6970d9b 100644 --- a/lib/srv/discovery/access_graph.go +++ b/lib/srv/discovery/access_graph.go @@ -212,8 +212,8 @@ func push( } // NewAccessGraphClient returns a new access graph service client. -func newAccessGraphClient(ctx context.Context, certs []tls.Certificate, config AccessGraphConfig, opts ...grpc.DialOption) (*grpc.ClientConn, error) { - opt, err := grpcCredentials(config, certs) +func newAccessGraphClient(ctx context.Context, getCert func() (*tls.Certificate, error), config AccessGraphConfig, opts ...grpc.DialOption) (*grpc.ClientConn, error) { + opt, err := grpcCredentials(config, getCert) if err != nil { return nil, trace.Wrap(err) } @@ -308,7 +308,7 @@ func (s *Server) initializeAndWatchAccessGraph(ctx context.Context, reloadCh <-c accessGraphConn, err := newAccessGraphClient( ctx, - s.ServerCredentials.Certificates, + s.GetClientCert, config, grpc.WithDefaultServiceConfig(serviceConfig), ) @@ -371,7 +371,7 @@ func (s *Server) initializeAndWatchAccessGraph(ctx context.Context, reloadCh <-c } // grpcCredentials returns a grpc.DialOption configured with TLS credentials. -func grpcCredentials(config AccessGraphConfig, certs []tls.Certificate) (grpc.DialOption, error) { +func grpcCredentials(config AccessGraphConfig, getCert func() (*tls.Certificate, error)) (grpc.DialOption, error) { var pool *x509.CertPool if len(config.CA) > 0 { pool = x509.NewCertPool() @@ -380,8 +380,15 @@ func grpcCredentials(config AccessGraphConfig, certs []tls.Certificate) (grpc.Di } } + // TODO(espadolini): this doesn't honor the process' configured ciphersuites tlsConfig := &tls.Config{ - Certificates: certs, + GetClientCertificate: func(*tls.CertificateRequestInfo) (*tls.Certificate, error) { + tlsCert, err := getCert() + if err != nil { + return nil, trace.Wrap(err) + } + return tlsCert, nil + }, MinVersion: tls.VersionTLS13, InsecureSkipVerify: config.Insecure, RootCAs: pool, diff --git a/lib/srv/discovery/discovery.go b/lib/srv/discovery/discovery.go index dcf4da61cee47..0e10172e6a3fa 100644 --- a/lib/srv/discovery/discovery.go +++ b/lib/srv/discovery/discovery.go @@ -138,9 +138,9 @@ type Config struct { // Default: [github.com/gravitational/teleport/lib/srv/discovery/common.DefaultDiscoveryPollInterval] PollInterval time.Duration - // ServerCredentials are the credentials used to identify the discovery service + // GetClientCert returns credentials used to identify the discovery service // to the Access Graph service. - ServerCredentials *tls.Config + GetClientCert func() (*tls.Certificate, error) // AccessGraphConfig is the configuration for the Access Graph client AccessGraphConfig AccessGraphConfig diff --git a/lib/srv/discovery/fetchers/aws-sync/ec2.go b/lib/srv/discovery/fetchers/aws-sync/ec2.go index e2962cfc346b7..b6a7be2ecb424 100644 --- a/lib/srv/discovery/fetchers/aws-sync/ec2.go +++ b/lib/srv/discovery/fetchers/aws-sync/ec2.go @@ -90,7 +90,7 @@ func (a *awsFetcher) fetchAWSEC2Instances(ctx context.Context) ([]*accessgraphv1 lHosts := make([]*accessgraphv1alpha.AWSInstanceV1, 0, len(page.Reservations)) for _, reservation := range page.Reservations { for _, instance := range reservation.Instances { - hosts = append(hosts, awsInstanceToProtoInstance(instance, a.AccountID)) + hosts = append(hosts, awsInstanceToProtoInstance(instance, region, a.AccountID)) } } collectHosts(lHosts, nil) @@ -110,7 +110,7 @@ func (a *awsFetcher) fetchAWSEC2Instances(ctx context.Context) ([]*accessgraphv1 // awsInstanceToProtoInstance converts an ec2.Instance to accessgraphv1alpha.AWSInstanceV1 // representation. -func awsInstanceToProtoInstance(instance *ec2.Instance, accountID string) *accessgraphv1alpha.AWSInstanceV1 { +func awsInstanceToProtoInstance(instance *ec2.Instance, region string, accountID string) *accessgraphv1alpha.AWSInstanceV1 { var tags []*accessgraphv1alpha.AWSTag for _, tag := range instance.Tags { tags = append(tags, &accessgraphv1alpha.AWSTag{ @@ -125,7 +125,7 @@ func awsInstanceToProtoInstance(instance *ec2.Instance, accountID string) *acces } return &accessgraphv1alpha.AWSInstanceV1{ InstanceId: aws.ToString(instance.InstanceId), - Region: aws.ToString(instance.Placement.AvailabilityZone), + Region: region, PublicDnsName: aws.ToString(instance.PublicDnsName), LaunchKeyName: strPtrToWrapper(instance.KeyName), IamInstanceProfileArn: instanceProfileMetadata, diff --git a/lib/srv/regular/sshserver_test.go b/lib/srv/regular/sshserver_test.go index 5654ce7d47af0..c818a9eb53557 100644 --- a/lib/srv/regular/sshserver_test.go +++ b/lib/srv/regular/sshserver_test.go @@ -21,6 +21,7 @@ package regular import ( "bytes" "context" + "crypto/tls" "encoding/json" "fmt" "io" @@ -1488,8 +1489,10 @@ func TestProxyRoundRobin(t *testing.T) { caWatcher := newCertAuthorityWatcher(ctx, t, proxyClient) reverseTunnelServer, err := reversetunnel.NewServer(reversetunnel.Config{ + GetClientTLSCertificate: func() (*tls.Certificate, error) { + return &proxyClient.TLSConfig().Certificates[0], nil + }, ClusterName: f.testSrv.ClusterName(), - ClientTLS: proxyClient.TLSConfig(), ID: hostID, Listener: listener, GetHostSigners: sshutils.StaticHostSigners(f.signer), @@ -1625,7 +1628,9 @@ func TestProxyDirectAccess(t *testing.T) { caWatcher := newCertAuthorityWatcher(ctx, t, proxyClient) reverseTunnelServer, err := reversetunnel.NewServer(reversetunnel.Config{ - ClientTLS: proxyClient.TLSConfig(), + GetClientTLSCertificate: func() (*tls.Certificate, error) { + return &proxyClient.TLSConfig().Certificates[0], nil + }, ID: hostID, ClusterName: f.testSrv.ClusterName(), Listener: listener, @@ -2338,7 +2343,9 @@ func TestParseSubsystemRequest(t *testing.T) { caWatcher := newCertAuthorityWatcher(ctx, t, proxyClient) reverseTunnelServer, err := reversetunnel.NewServer(reversetunnel.Config{ - ClientTLS: proxyClient.TLSConfig(), + GetClientTLSCertificate: func() (*tls.Certificate, error) { + return &proxyClient.TLSConfig().Certificates[0], nil + }, ID: hostID, ClusterName: f.testSrv.ClusterName(), Listener: listener, @@ -2600,7 +2607,9 @@ func TestIgnorePuTTYSimpleChannel(t *testing.T) { caWatcher := newCertAuthorityWatcher(ctx, t, proxyClient) reverseTunnelServer, err := reversetunnel.NewServer(reversetunnel.Config{ - ClientTLS: proxyClient.TLSConfig(), + GetClientTLSCertificate: func() (*tls.Certificate, error) { + return &proxyClient.TLSConfig().Certificates[0], nil + }, ID: hostID, ClusterName: f.testSrv.ClusterName(), Listener: listener, diff --git a/lib/srv/transport/transportv1/transport.go b/lib/srv/transport/transportv1/transport.go index 756083eab30f0..f019875186af7 100644 --- a/lib/srv/transport/transportv1/transport.go +++ b/lib/srv/transport/transportv1/transport.go @@ -29,8 +29,10 @@ import ( "github.com/gravitational/trace" "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh/agent" + "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" "google.golang.org/grpc/peer" + "google.golang.org/grpc/status" "github.com/gravitational/teleport" transportv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/transport/v1" @@ -244,7 +246,7 @@ func (s *Service) ProxySSH(stream transportv1pb.TransportService_ProxySSHServer) for { req, err := stream.Recv() if err != nil { - if !utils.IsOKNetworkError(err) && !errors.Is(err, context.Canceled) { + if !utils.IsOKNetworkError(err) && !errors.Is(err, context.Canceled) && status.Code(err) != codes.Canceled { s.cfg.Logger.Errorf("ssh stream terminated unexpectedly: %v", err) } diff --git a/lib/tbot/config/destination_directory.go b/lib/tbot/config/destination_directory.go index df1fae33fba10..6287f4f40b93d 100644 --- a/lib/tbot/config/destination_directory.go +++ b/lib/tbot/config/destination_directory.go @@ -161,9 +161,27 @@ func (dd *DestinationDirectory) Init(_ context.Context, subdirs []string) error } func (dd *DestinationDirectory) Verify(keys []string) error { + // If ACLs are disabled or unsupported, just bail as there's nothing to + // check. + if dd.ACLs == botfs.ACLOff || !botfs.HasACLSupport() { + return nil + } + currentUser, err := user.Current() if err != nil { - return trace.Wrap(err) + // user.Current will fail if the user id does not exist in /etc/passwd + // as is the case with some containerized environments. + // TODO(noah): Switch to os.Getuid / handling UIDs directly. + if dd.ACLs == botfs.ACLRequired { + return trace.Wrap(err, "determining current user") + } + log.WarnContext( + context.TODO(), + "Unable to determine current user, ACLs will not be checked. To silence this warning, set ACL mode to `off`.", + "path", dd.Path, + "error", err, + ) + return nil } stat, err := os.Stat(dd.Path) @@ -180,10 +198,10 @@ func (dd *DestinationDirectory) Verify(keys []string) error { return trace.Wrap(err) } - // Make sure it's worth warning about ACLs for this Destination. If ACLs - // are disabled, unsupported, or the Destination is owned by the bot - // (implying the user is not trying to use ACLs), just bail. - if dd.ACLs == botfs.ACLOff || !botfs.HasACLSupport() || ownedByBot { + // Make sure it's worth warning about ACLs for this Destination. If the + // destination is owned by the bot (implying the user is not trying to use + //ACLs), just bail. + if ownedByBot { return nil } diff --git a/lib/tbot/output_utils.go b/lib/tbot/output_utils.go index f4e8ddbdd2943..3f78663b7b73d 100644 --- a/lib/tbot/output_utils.go +++ b/lib/tbot/output_utils.go @@ -276,9 +276,16 @@ func describeTLSIdentity(ctx context.Context, log *slog.Logger, ident *identity. } } + botDesc := "" + if tlsIdent.BotInstanceID != "" { + botDesc = fmt.Sprintf(", id=%s", tlsIdent.BotInstanceID) + } + duration := cert.NotAfter.Sub(cert.NotBefore) return fmt.Sprintf( - "valid: after=%v, before=%v, duration=%s | kind=tls, renewable=%v, disallow-reissue=%v, roles=%v, principals=%v, generation=%v", + "%s%s | valid: after=%v, before=%v, duration=%s | kind=tls, renewable=%v, disallow-reissue=%v, roles=%v, principals=%v, generation=%v", + tlsIdent.BotName, + botDesc, cert.NotBefore.Format(time.RFC3339), cert.NotAfter.Format(time.RFC3339), duration, diff --git a/lib/tbot/service_ssh_multiplexer.go b/lib/tbot/service_ssh_multiplexer.go index fa39915614abe..ea5fab6adf266 100644 --- a/lib/tbot/service_ssh_multiplexer.go +++ b/lib/tbot/service_ssh_multiplexer.go @@ -358,6 +358,19 @@ func (s *SSHMultiplexerService) generateIdentity(ctx context.Context) (*identity if err != nil { return nil, trace.Wrap(err, "adding identity to agent") } + // There's a bug with Paramiko and older versions of OpenSSH that requires + // that the bare key also be included in the agent or the key with the + // certificate will not be used. + // See the following: https://bugzilla.mindrot.org/show_bug.cgi?id=2550 + err = newAgent.Add(agent.AddedKey{ + PrivateKey: id.PrivateKey, + Certificate: nil, + LifetimeSecs: 0, + }) + if err != nil { + return nil, trace.Wrap(err, "adding bare key to agent") + } + s.agentMu.Lock() s.agent = newAgent.(agent.ExtendedAgent) s.agentMu.Unlock() diff --git a/lib/tbot/tbot_test.go b/lib/tbot/tbot_test.go index ebe10213c5aa7..1b6faa4cb7972 100644 --- a/lib/tbot/tbot_test.go +++ b/lib/tbot/tbot_test.go @@ -1081,6 +1081,12 @@ func TestBotSSHMultiplexer(t *testing.T) { out, err := sshSess.CombinedOutput("echo hello") require.NoError(t, err) require.Equal(t, "hello\n", string(out)) + + // Check that the agent presents a key with cert and a bare key + // for compat with Paramiko and older versions of OpenSSH. + keys, err := agentClient.List() + require.NoError(t, err) + require.Len(t, keys, 2) }) } } diff --git a/lib/teleterm/daemon/daemon_test.go b/lib/teleterm/daemon/daemon_test.go index 4046043d805d0..827a9c13cba9c 100644 --- a/lib/teleterm/daemon/daemon_test.go +++ b/lib/teleterm/daemon/daemon_test.go @@ -581,7 +581,7 @@ func TestImportantModalSemaphore(t *testing.T) { } type mockTSHDEventsService struct { - *api.UnimplementedTshdEventsServiceServer + api.UnimplementedTshdEventsServiceServer reloginErr error reloginCount atomic.Uint32 sendNotificationCount atomic.Uint32 diff --git a/lib/tlsca/ca.go b/lib/tlsca/ca.go index 3597902c5f3d8..2b589e28e8874 100644 --- a/lib/tlsca/ca.go +++ b/lib/tlsca/ca.go @@ -354,6 +354,8 @@ func (id *Identity) GetEventIdentity() events.Identity { AllowedResourceIDs: events.ResourceIDs(id.AllowedResourceIDs), PrivateKeyPolicy: string(id.PrivateKeyPolicy), DeviceExtensions: devExts, + BotName: id.BotName, + BotInstanceID: id.BotInstanceID, } } @@ -1135,6 +1137,8 @@ func (id Identity) GetUserMetadata() events.UserMetadata { AccessRequests: id.ActiveRequests, UserKind: userKind, TrustedDevice: device, + BotName: id.BotName, + BotInstanceID: id.BotInstanceID, } } diff --git a/lib/tlsca/ca_test.go b/lib/tlsca/ca_test.go index e38af89fdb8bf..d84dc20fdc71c 100644 --- a/lib/tlsca/ca_test.go +++ b/lib/tlsca/ca_test.go @@ -430,24 +430,15 @@ func TestIdentity_GetUserMetadata(t *testing.T) { { name: "user metadata for bot", identity: Identity{ - Username: "alpaca", - Impersonator: "llama", - RouteToApp: RouteToApp{ - AWSRoleARN: "awsrolearn", - AzureIdentity: "azureidentity", - GCPServiceAccount: "gcpaccount", - }, - ActiveRequests: []string{"accessreq1", "accessreq2"}, - BotName: "foo", + Username: "bot-alpaca", + BotName: "alpaca", + BotInstanceID: "123-123", }, want: apievents.UserMetadata{ - User: "alpaca", - Impersonator: "llama", - AWSRoleARN: "awsrolearn", - AccessRequests: []string{"accessreq1", "accessreq2"}, - AzureIdentity: "azureidentity", - GCPServiceAccount: "gcpaccount", - UserKind: apievents.UserKind_USER_KIND_BOT, + User: "bot-alpaca", + UserKind: apievents.UserKind_USER_KIND_BOT, + BotName: "alpaca", + BotInstanceID: "123-123", }, }, { diff --git a/lib/uds/cred_test.go b/lib/uds/cred_test.go index fa296ef847e70..d496758db3bbd 100644 --- a/lib/uds/cred_test.go +++ b/lib/uds/cred_test.go @@ -76,7 +76,7 @@ func TestGetCreds(t *testing.T) { } type service struct { - *machineidv1.UnimplementedBotServiceServer + machineidv1.UnimplementedBotServiceServer lastCalledCreds *Creds } diff --git a/lib/utils/tls.go b/lib/utils/tls.go index 223be4f2d7e8a..7365d27a6334a 100644 --- a/lib/utils/tls.go +++ b/lib/utils/tls.go @@ -21,6 +21,7 @@ package utils import ( "context" "crypto/tls" + "crypto/x509" "net" "time" @@ -64,6 +65,63 @@ func CipherSuiteMapping(cipherSuites []string) ([]uint16, error) { return out, nil } +// VerifyConnectionWithRoots returns a [tls.Config.VerifyConnection] function +// that uses the provided function to generate a [*x509.CertPool] that's used as +// the source of root CAs for verification. Use of this function requires a +// modicum of care: the [*tls.Config] using the returned callback should be +// generated as close to its point of use as possible. An example use for this +// would be something like: +// +// c := utils.TLSConfig(cfg.cipherSuites) +// c.GetClientCertificate = func(cri *tls.CertificateRequestInfo) (*tls.Certificate, error) { +// return cfg.getCert() +// } +// c.ServerName = apiutils.EncodeClusterName(cfg.clusterName) +// c.InsecureSkipVerify = true +// c.VerifyConnection = VerifyConnectionWithRoots(cfg.getRoots) +// httpTransport.TLSClientConfig = c +// clientConn := grpc.NewClient(target, grpc.WithTransportCredentials(credentials.NewTLS(c))) +// +// The necessity of using InsecureSkipVerify is the reason why this construction +// is deliberately not packaged into a utility function, as the stakes must be +// clear to whoever is interacting with the constructed [*tls.Config]. The +// recommended approach is to push the getter functions as close to the point of +// use as possible. +func VerifyConnectionWithRoots(getRoots func() (*x509.CertPool, error)) func(cs tls.ConnectionState) error { + return func(cs tls.ConnectionState) error { + if cs.ServerName == "" { + return trace.BadParameter("TLS verification requires a server name") + } + roots, err := getRoots() + if err != nil { + return trace.Wrap(err) + } + + opts := x509.VerifyOptions{ + Roots: roots, + Intermediates: nil, + + DNSName: cs.ServerName, + } + if len(cs.PeerCertificates) > 1 { + opts.Intermediates = x509.NewCertPool() + for _, cert := range cs.PeerCertificates[1:] { + opts.Intermediates.AddCert(cert) + } + } + if _, err := cs.PeerCertificates[0].Verify(opts); err != nil { + return trace.Wrap(err) + } + + return nil + } +} + +type ( + GetCertificateFunc = func() (*tls.Certificate, error) + GetRootsFunc = func() (*x509.CertPool, error) +) + // TLSConn is a `net.Conn` that implements some of the functions defined by the // `tls.Conn` struct. This interface can be used where it could receive a // `tls.Conn` wrapped in another connection. For example, in the ALPN Proxy, diff --git a/lib/vnet/daemon/client_darwin.go b/lib/vnet/daemon/client_darwin.go index 3aa1e7320ad21..9c53d8a7ee580 100644 --- a/lib/vnet/daemon/client_darwin.go +++ b/lib/vnet/daemon/client_darwin.go @@ -19,11 +19,9 @@ package daemon -// #cgo CFLAGS: -Wall -xobjective-c -fblocks -fobjc-arc -mmacosx-version-min=10.15 -// #cgo LDFLAGS: -framework Foundation -framework ServiceManagement // #include // #include "client_darwin.h" -// #include "protocol_darwin.h" +// #include "common_darwin.h" import "C" import ( @@ -304,6 +302,28 @@ func startByCalling(ctx context.Context, bundlePath string, config Config) error return } + if errorDomain == nsCocoaErrorDomain && errorCode == errorCodeNSXPCConnectionInterrupted { + const clientNSXPCConnectionInterruptedDebugMsg = "The connection was interrupted when trying to " + + "reach the XPC service. If there's no clear error logs on the daemon side, it might mean that " + + "the client does not satisfy the code signing requirement enforced by the daemon. " + + "Start capturing logs in Console.app and repeat the scenario. Look for " + + "\"xpc_support_check_token: error: status: -67050\" in the logs to verify " + + "that the connection was interrupted due to the code signing requirement." + log.DebugContext(ctx, clientNSXPCConnectionInterruptedDebugMsg) + errC <- trace.Wrap(errXPCConnectionInterrupted) + return + } + + if errorDomain == vnetErrorDomain && errorCode == errorCodeMissingCodeSigningIdentifiers { + errC <- trace.Wrap(errMissingCodeSigningIdentifiers) + return + } + + if errorDomain == nsCocoaErrorDomain && errorCode == errorCodeNSXPCConnectionCodeSigningRequirementFailure { + errC <- trace.Wrap(errXPCConnectionCodeSigningRequirementFailure, "the daemon does not appear to be code signed correctly") + return + } + errC <- trace.Errorf("could not start VNet daemon: %v", C.GoString(res.error_description)) return } @@ -319,15 +339,6 @@ func startByCalling(ctx context.Context, bundlePath string, config Config) error } } -var ( - // vnetErrorDomain is a custom error domain used for Objective-C errors that pertain to VNet. - vnetErrorDomain = C.GoString(C.VNEErrorDomain) - // errorCodeAlreadyRunning is returned within [vnetErrorDomain] errors to indicate that the daemon - // received a message to start after it was already running. - errorCodeAlreadyRunning = int(C.VNEAlreadyRunningError) - errAlreadyRunning = errors.New("VNet is already running") -) - func sleepOrDone(ctx context.Context, d time.Duration) error { timer := time.NewTimer(d) defer timer.Stop() diff --git a/lib/vnet/daemon/client_darwin.h b/lib/vnet/daemon/client_darwin.h index a7f5e4c8bdb1c..e362c4add527b 100644 --- a/lib/vnet/daemon/client_darwin.h +++ b/lib/vnet/daemon/client_darwin.h @@ -2,7 +2,6 @@ #define TELEPORT_LIB_VNET_DAEMON_CLIENT_DARWIN_H_ #include "common_darwin.h" -#include "protocol_darwin.h" #import @@ -44,10 +43,15 @@ typedef struct StartVnetRequest { typedef struct StartVnetResult { bool ok; + // error_domain is either VNEErrorDomain, NSOSStatusErrorDomain, or NSCocoaErrorDomain. const char *error_domain; - // error_code is code taken from an NSError instance encountered during the call to StartVnet. - // If ok is false, error_code is greater than zero and identifies the reason behind the error. + // If error_domain is set to VNEErrorDomain, error_code is one of the VNE codes from common_darwin.h. + // If error_domain is NSOSStatusErrorDomain, error_code comes from OSStatus of Code Signing framework. + // https://developer.apple.com/documentation/security/1574088-code_signing_services_result_cod?language=objc + // If error_domain is NSCocoaErrorDomain, it's likely to be about XPC. It's best to inspect it + // on https://osstatus.com in that case. int error_code; + // error_description includes the full representation of the error, including domain and code. const char *error_description; } StartVnetResult; diff --git a/lib/vnet/daemon/client_darwin.m b/lib/vnet/daemon/client_darwin.m index d2fd9256ead73..b131926d04038 100644 --- a/lib/vnet/daemon/client_darwin.m +++ b/lib/vnet/daemon/client_darwin.m @@ -19,7 +19,6 @@ #include "client_darwin.h" #include "common_darwin.h" -#include "protocol_darwin.h" #import #import @@ -78,15 +77,17 @@ @interface VNEDaemonClient () @property(nonatomic, strong, readwrite) NSXPCConnection *connection; @property(nonatomic, strong, readonly) NSString *bundlePath; +@property(nonatomic, strong, readonly) NSString *codeSigningRequirement; @end @implementation VNEDaemonClient -- (id)initWithBundlePath:(NSString *)bundlePath { +- (id)initWithBundlePath:(NSString *)bundlePath codeSigningRequirement:(NSString *)codeSigningRequirement { self = [super init]; if (self) { _bundlePath = bundlePath; + _codeSigningRequirement = codeSigningRequirement; } return self; } @@ -102,6 +103,12 @@ - (NSXPCConnection *)connection { self->_connection = nil; }; + // The daemon won't even be started on macOS < 13.0, so we don't have to handle the else branch + // of this condition. + if (@available(macOS 13, *)) { + [_connection setCodeSigningRequirement:_codeSigningRequirement]; + } + // New connections always start in a suspended state. [_connection resume]; } @@ -134,7 +141,18 @@ - (void)invalidate { void StartVnet(StartVnetRequest *request, StartVnetResult *outResult) { if (!daemonClient) { - daemonClient = [[VNEDaemonClient alloc] initWithBundlePath:@(request->bundle_path)]; + NSString *requirement = nil; + NSError *error = nil; + bool ok = getCodeSigningRequirement(&requirement, &error); + if (!ok) { + outResult->ok = false; + outResult->error_domain = VNECopyNSString([error domain]); + outResult->error_code = (int)[error code]; + outResult->error_description = VNECopyNSString([error description]); + return; + } + + daemonClient = [[VNEDaemonClient alloc] initWithBundlePath:@(request->bundle_path) codeSigningRequirement:requirement]; } dispatch_semaphore_t sema = dispatch_semaphore_create(0); @@ -142,6 +160,7 @@ void StartVnet(StartVnetRequest *request, StartVnetResult *outResult) { [daemonClient startVnet:request->vnet_config completion:^(NSError *error) { if (error) { + outResult->ok = false; outResult->error_domain = VNECopyNSString([error domain]); outResult->error_code = (int)[error code]; outResult->error_description = VNECopyNSString([error description]); diff --git a/lib/vnet/daemon/common_darwin.go b/lib/vnet/daemon/common_darwin.go new file mode 100644 index 0000000000000..1daae730aa9a3 --- /dev/null +++ b/lib/vnet/daemon/common_darwin.go @@ -0,0 +1,57 @@ +// 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 . + +//go:build vnetdaemon +// +build vnetdaemon + +package daemon + +// #cgo CFLAGS: -Wall -xobjective-c -fblocks -fobjc-arc -mmacosx-version-min=10.15 +// #cgo LDFLAGS: -framework Foundation -framework ServiceManagement +// #include "common_darwin.h" +import "C" + +import ( + "errors" +) + +var ( + // vnetErrorDomain is a custom error domain used for Objective-C errors that pertain to VNet. + vnetErrorDomain = C.GoString(C.VNEErrorDomain) + + // errorCodeAlreadyRunning is returned within [vnetErrorDomain] errors to indicate that the daemon + // received a message to start after it was already running. + errorCodeAlreadyRunning = int(C.VNEAlreadyRunningError) + errAlreadyRunning = errors.New("VNet is already running") + + // errorCodeMissingCodeSigningIdentifiers is returned within [vnetErrorDomain] Obj-C errors and + // transformed to [errMissingCodeSigningIdentifiers] in Go. + errorCodeMissingCodeSigningIdentifiers = int(C.VNEMissingCodeSigningIdentifiersError) + errMissingCodeSigningIdentifiers = errors.New("either identifier or team identifier is missing in code signing information; is the binary signed?") +) + +var ( + // nsCocoaErrorDomain is a generic error domain used in a lot of Apple's Cocoa frameworks. + nsCocoaErrorDomain = "NSCocoaErrorDomain" + + // https://developer.apple.com/documentation/foundation/1448136-nserror_codes/nsxpcconnectioninterrupted?changes=latest_major&language=objc + errorCodeNSXPCConnectionInterrupted = int(C.NSXPCConnectionInterrupted) + errXPCConnectionInterrupted = errors.New("XPC connection interrupted") + + // https://developer.apple.com/documentation/foundation/1448136-nserror_codes/nsxpcconnectioncodesigningrequirementfailure?language=objc + errorCodeNSXPCConnectionCodeSigningRequirementFailure = int(C.NSXPCConnectionCodeSigningRequirementFailure) + errXPCConnectionCodeSigningRequirementFailure = errors.New("code signing requirement failed") +) diff --git a/lib/vnet/daemon/common_darwin.h b/lib/vnet/daemon/common_darwin.h index f549ca1b8ef79..92c73b310f4b3 100644 --- a/lib/vnet/daemon/common_darwin.h +++ b/lib/vnet/daemon/common_darwin.h @@ -3,6 +3,35 @@ #import +// VNEErrorDomain is a custom error domain used for Objective-C errors that pertain to VNet. +extern const char* const VNEErrorDomain; + +// VNEAlreadyRunningError indicates that the daemon already received a VNet config. +// It won't accept a new one during its lifetime, instead it's expected to stop, after +// which the client might spawn a new instance of the daemon. +extern const int VNEAlreadyRunningError; +// VNEMissingCodeSigningIdentifiersError indicates that either the identifier or the team identifier are missing. +// This can happen if the binary is unsigned, see the docs for SecCodeCopySigningInformation. +// https://developer.apple.com/documentation/security/1395809-seccodecopysigninginformation?language=objc +extern const int VNEMissingCodeSigningIdentifiersError; + +typedef struct VnetConfig { + const char *socket_path; + const char *ipv6_prefix; + const char *dns_addr; + const char *home_path; +} VnetConfig; + +@protocol VNEDaemonProtocol +// startVnet passes the config back to Go code (which then starts VNet in a separate thread) +// and returns immediately. +// +// Only the first call to this method starts VNet. Subsequent calls return VNEAlreadyRunningError. +// The daemon process exits after VNet is stopped, after which it can be spawned again by calling +// this method. +- (void)startVnet:(VnetConfig *)vnetConfig completion:(void (^)(NSError *error))completion; +@end + // Returns the label for the daemon by getting the identifier of the bundle // this executable is shipped in and appending ".vnetd" to it. // @@ -16,4 +45,15 @@ NSString *DaemonLabel(NSString *bundlePath); // The caller is expected to free the returned pointer. const char *VNECopyNSString(NSString *val); +// getCodeSigningRequirement calculates the requirement that will be matched against +// the designated requirement of the app on the other side of an XPC connection. +// It does so based on the code signing information of the current binary, as it assumes that +// both the VNet client and the VNet daemon use the same binary. +// +// On success, it returns true and sets outRequirement. +// On error, it returns false and sets outError. Returns errors of VNEErrorDomain and +// NSOSStatusErrorDomain. Errors with the latter domain are likely to match Code Signing OSStatus values. +// https://developer.apple.com/documentation/security/1574088-code_signing_services_result_cod?language=objc +bool getCodeSigningRequirement(NSString **outRequirement, NSError **outError); + #endif /* TELEPORT_LIB_VNET_DAEMON_COMMON_DARWIN_H_ */ diff --git a/lib/vnet/daemon/common_darwin.m b/lib/vnet/daemon/common_darwin.m index 194ac7f0f0f2e..3a405209893f8 100644 --- a/lib/vnet/daemon/common_darwin.m +++ b/lib/vnet/daemon/common_darwin.m @@ -17,10 +17,17 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +#import #import +#import #include +const char* const VNEErrorDomain = "com.Gravitational.Vnet.ErrorDomain"; + +const int VNEAlreadyRunningError = 1; +const int VNEMissingCodeSigningIdentifiersError = 2; + NSString *DaemonLabel(NSString *bundlePath) { NSBundle *main = [NSBundle bundleWithPath:bundlePath]; if (!main) { @@ -41,3 +48,73 @@ } return strdup(""); } + +bool getCodeSigningRequirement(NSString **outRequirement, NSError **outError) { + SecCodeRef codeObj = nil; + OSStatus status = SecCodeCopySelf(kSecCSDefaultFlags, &codeObj); + if (status != errSecSuccess) { + if (outError) { + *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; + } + return false; + } + + CFDictionaryRef cfCodeSignInfo = nil; + // kSecCSSigningInformation must be provided as a flag for the team identifier to be included + // in the returned dictionary. + status = SecCodeCopySigningInformation(codeObj, kSecCSSigningInformation, &cfCodeSignInfo); + // codeObj is no longer needed. Manually release it, as we own it since we got it from a function + // with "Copy" in its name. + // https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/Concepts/Ownership.html#//apple_ref/doc/writerid/cfCreateRule + CFRelease(codeObj); + if (status != errSecSuccess) { + if (outError) { + *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; + } + return false; + } + + // Transfer ownership of cfCodeSignInfo to Obj-C, which means we don't have to CFRelease it manually. + // We can transfer the ownership of cfCodeSignInfo because we own it (we got it from a function + // with "Copy" in its name). + // https://developer.apple.com/documentation/foundation/1587932-cfbridgingrelease + NSDictionary *codeSignInfo = (NSDictionary *)CFBridgingRelease(cfCodeSignInfo); + // We don't own kSecCodeInfoIdentifier, so we cannot call CFBridgingRelease on it. + // __bridge transfers a pointer between Obj-C and CoreFoundation with no transfer of ownership. + // Values extracted out of codeSignInfo are cast to toll-free bridged Obj-C types. + // https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFDesignConcepts/Articles/tollFreeBridgedTypes.html#//apple_ref/doc/uid/TP40010677-SW2 + // https://stackoverflow.com/questions/18067108/when-should-you-use-bridge-vs-cfbridgingrelease-cfbridgingretain + NSString *identifier = codeSignInfo[(__bridge NSString *)kSecCodeInfoIdentifier]; + NSString *teamIdentifier = codeSignInfo[(__bridge NSString *)kSecCodeInfoTeamIdentifier]; + + if (!identifier || [identifier length] == 0 || !teamIdentifier || [teamIdentifier length] == 0) { + if (outError) { + *outError = [NSError errorWithDomain:@(VNEErrorDomain) code:VNEMissingCodeSigningIdentifiersError userInfo:nil]; + } + return false; + } + + // The requirement will be matched against the designated requirement of the application on + // the other side of an XPC connection. It is based on the designated requirement of tsh.app. + // To inspect the designated requirement of an app, use the following command: + // + // codesign --display -r - + // + // Breakdown of individual parts of the requirement: + // * `identifier "foo"` is satisfied if the code signing identifier matches the provided one. + // It is not the same as the bundle identifier. + // * `anchor apple generic` is satisfied by any code signed with any code signing identity issued + // by Apple. + // * `certificate leaf[field(bunch of specific numbers)]` is satisfied by code signed with + // Developer ID Application certs. + // * `certificate leaf[subject.OU]` is satisfied by certs with a specific Team ID. + // + // Read more at: + // https://developer.apple.com/documentation/technotes/tn3127-inside-code-signing-requirements#Designated-requirement + // https://developer.apple.com/documentation/technotes/tn3127-inside-code-signing-requirements#Xcode-designated-requirement-for-Developer-ID-code + if (outRequirement) { + *outRequirement = [NSString stringWithFormat:@"identifier \"%@\" and anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.13] and certificate leaf[subject.OU] = %@", identifier, teamIdentifier]; + } + + return true; +} diff --git a/lib/vnet/daemon/protocol_darwin.h b/lib/vnet/daemon/protocol_darwin.h deleted file mode 100644 index d150bb99d37a4..0000000000000 --- a/lib/vnet/daemon/protocol_darwin.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef TELEPORT_LIB_VNET_DAEMON_PROTOCOL_DARWIN_H_ -#define TELEPORT_LIB_VNET_DAEMON_PROTOCOL_DARWIN_H_ - -#import - -// VNEErrorDomain is a custom error domain used for Objective-C errors that pertain to VNet. -extern const char* const VNEErrorDomain; - -// VNEAlreadyRunningError indicates that the daemon already received a VNet config. -// It won't accept a new one during its lifetime, instead it's expected to stop, after -// which the client might spawn a new instance of the daemon. -extern const int VNEAlreadyRunningError; - -typedef struct VnetConfig { - const char *socket_path; - const char *ipv6_prefix; - const char *dns_addr; - const char *home_path; -} VnetConfig; - -@protocol VNEDaemonProtocol -// startVnet passes the config back to Go code (which then starts VNet in a separate thread) -// and returns immediately. -// -// Only the first call to this method starts VNet. Subsequent calls return VNEAlreadyRunningError. -// The daemon process exits after VNet is stopped, after which it can be spawned again by calling -// this method. -- (void)startVnet:(VnetConfig *)vnetConfig completion:(void (^)(NSError *error))completion; -@end - -#endif /* TELEPORT_LIB_VNET_DAEMON_PROTOCOL_DARWIN_H_ */ diff --git a/lib/vnet/daemon/service_darwin.go b/lib/vnet/daemon/service_darwin.go index cf996097efe3f..bd7db4096da06 100644 --- a/lib/vnet/daemon/service_darwin.go +++ b/lib/vnet/daemon/service_darwin.go @@ -19,8 +19,6 @@ package daemon -// #cgo CFLAGS: -Wall -xobjective-c -fblocks -fobjc-arc -mmacosx-version-min=10.15 -// #cgo LDFLAGS: -framework Foundation // #include // #include "service_darwin.h" import "C" @@ -47,7 +45,23 @@ func Start(ctx context.Context, workFn func(context.Context, Config) error) erro cBundlePath := C.CString(bundlePath) defer C.free(unsafe.Pointer(cBundlePath)) - C.DaemonStart(cBundlePath) + var result C.DaemonStartResult + defer func() { + C.free(unsafe.Pointer(result.error_domain)) + C.free(unsafe.Pointer(result.error_description)) + }() + C.DaemonStart(cBundlePath, &result) + if !result.ok { + errorDomain := C.GoString(result.error_domain) + errorCode := int(result.error_code) + + if errorDomain == vnetErrorDomain && errorCode == errorCodeMissingCodeSigningIdentifiers { + return trace.Wrap(errMissingCodeSigningIdentifiers) + } + + return trace.Errorf("could not start daemon: %s", C.GoString(result.error_description)) + } + defer func() { log.InfoContext(ctx, "Stopping daemon") C.DaemonStop() diff --git a/lib/vnet/daemon/service_darwin.h b/lib/vnet/daemon/service_darwin.h index 779c33585b8a4..fd39979eed25f 100644 --- a/lib/vnet/daemon/service_darwin.h +++ b/lib/vnet/daemon/service_darwin.h @@ -5,7 +5,7 @@ @interface VNEDaemonService : NSObject -- (id)initWithBundlePath:(NSString *)bundlePath; +- (id)initWithBundlePath:(NSString *)bundlePath codeSigningRequirement:(NSString *)codeSigningRequirement; // start begins listening for incoming XPC connections. - (void)start; @@ -19,9 +19,23 @@ @end +typedef struct DaemonStartResult { + bool ok; + // error_domain is set to either VNEErrorDomain or NSOSStatusErrorDomain if ok is false. + const char *error_domain; + // If error_domain is set to VNEErrorDomain, error_code is one of the VNE codes from common_darwin.h. + // If error_domain is NSOSStatusErrorDomain, error_code comes from OSStatus of Code Signing framework. + // https://developer.apple.com/documentation/security/1574088-code_signing_services_result_cod?language=objc + int error_code; + // error_description includes the full representation of the error, including domain and code. + const char *error_description; +} DaemonStartResult; + // DaemonStart initializes the XPC service and starts listening for new connections. // It's expected to be called only once, noop if the daemon was already started. -void DaemonStart(const char *bundle_path); +// It might fail if it runs into problems with Code Signing APIs while calucating the code signing +// requirement. In such case, outResult.ok is set to false and the error fields are populated. +void DaemonStart(const char *bundle_path, DaemonStartResult *outResult); // DaemonStop stops the XPC service. Noop if DaemonStart wasn't called. void DaemonStop(void); diff --git a/lib/vnet/daemon/service_darwin.m b/lib/vnet/daemon/service_darwin.m index 8258254eaf249..01b636f72fe6a 100644 --- a/lib/vnet/daemon/service_darwin.m +++ b/lib/vnet/daemon/service_darwin.m @@ -18,7 +18,6 @@ // along with this program. If not, see . #include "common_darwin.h" -#include "protocol_darwin.h" #include "service_darwin.h" #import @@ -44,14 +43,19 @@ @interface VNEDaemonService () @implementation VNEDaemonService -- (id)initWithBundlePath:(NSString *)bundlePath { +- (id)initWithBundlePath:(NSString *)bundlePath codeSigningRequirement:(NSString *)codeSigningRequirement { self = [super init]; if (self) { - // Launch daemons must configure their listener with the machServiceName - // initializer. + // Launch daemons must configure their listener with the machServiceName initializer. _listener = [[NSXPCListener alloc] initWithMachServiceName:DaemonLabel(bundlePath)]; _listener.delegate = self; + // The daemon won't even be started on macOS < 13.0, so we don't have to handle the else branch + // of this condition. + if (@available(macOS 13, *)) { + [_listener setConnectionCodeSigningRequirement:codeSigningRequirement]; + } + _started = NO; _gotVnetConfigSema = dispatch_semaphore_create(0); } @@ -126,12 +130,26 @@ - (BOOL)listener:(NSXPCListener *)listener static VNEDaemonService *daemonService = NULL; -void DaemonStart(const char *bundle_path) { +void DaemonStart(const char *bundle_path, DaemonStartResult *outResult) { if (daemonService) { + outResult->ok = true; + return; + } + + NSString *requirement = nil; + NSError *error = nil; + bool ok = getCodeSigningRequirement(&requirement, &error); + if (!ok) { + outResult->ok = false; + outResult->error_domain = VNECopyNSString([error domain]); + outResult->error_code = (int)[error code]; + outResult->error_description = VNECopyNSString([error description]); return; } - daemonService = [[VNEDaemonService alloc] initWithBundlePath:@(bundle_path)]; + + daemonService = [[VNEDaemonService alloc] initWithBundlePath:@(bundle_path) codeSigningRequirement:requirement]; [daemonService start]; + outResult->ok = true; } void DaemonStop(void) { diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index 5679d2dcc3201..1eb3d4502306e 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -227,8 +227,8 @@ type Config struct { ProxyWebAddr utils.NetAddr // ProxyPublicAddr contains web proxy public addresses. ProxyPublicAddrs []utils.NetAddr - // GetProxyClientTLSConfig returns the client TLS config of the proxy - GetProxyClientTLSConfig func(ciphersuites []uint16) (*tls.Config, error) + // GetProxyClientCertificate returns the proxy client certificate. + GetProxyClientCertificate func() (*tls.Certificate, error) // CipherSuites is the list of cipher suites Teleport suppports. CipherSuites []uint16 @@ -771,8 +771,13 @@ func (h *Handler) bindDefaultEndpoints() { h.GET("/webapi/auth/export", h.authExportPublic) // join token handlers - h.PUT("/webapi/token/yaml", h.WithAuth(h.upsertTokenContent)) - h.POST("/webapi/token", h.WithAuth(h.createTokenHandle)) + h.PUT("/webapi/tokens/yaml", h.WithAuth(h.updateTokenYAML)) + // used for creating a new token + h.POST("/webapi/tokens", h.WithAuth(h.upsertTokenHandle)) + // used for updating a token + h.PUT("/webapi/tokens", h.WithAuth(h.upsertTokenHandle)) + // used for creating tokens used during guided discover flows + h.POST("/webapi/token", h.WithAuth(h.createTokenForDiscoveryHandle)) h.GET("/webapi/tokens", h.WithAuth(h.getTokens)) h.DELETE("/webapi/tokens", h.WithAuth(h.deleteToken)) @@ -895,6 +900,7 @@ func (h *Handler) bindDefaultEndpoints() { h.POST("/webapi/sites/:site/integrations/aws-oidc/:name/ec2ice", h.WithClusterAuth(h.awsOIDCListEC2ICE)) h.POST("/webapi/sites/:site/integrations/aws-oidc/:name/deployec2ice", h.WithClusterAuth(h.awsOIDCDeployEC2ICE)) h.POST("/webapi/sites/:site/integrations/aws-oidc/:name/securitygroups", h.WithClusterAuth(h.awsOIDCListSecurityGroups)) + h.POST("/webapi/sites/:site/integrations/aws-oidc/:name/databasevpcs", h.WithClusterAuth(h.awsOIDCListDatabaseVPCs)) h.POST("/webapi/sites/:site/integrations/aws-oidc/:name/subnets", h.WithClusterAuth(h.awsOIDCListSubnets)) h.POST("/webapi/sites/:site/integrations/aws-oidc/:name/requireddatabasesvpcs", h.WithClusterAuth(h.awsOIDCRequiredDatabasesVPCS)) h.GET("/webapi/scripts/integrations/configure/eice-iam.sh", h.WithLimiter(h.awsOIDCConfigureEICEIAM)) @@ -986,13 +992,16 @@ func (h *Handler) GetProxyClient() authclient.ClientI { return h.cfg.ProxyClient } -// GetProxyClientTLSConfig returns the client TLS config of the proxy -func (h *Handler) GetProxyClientTLSConfig(ciphersuites []uint16) (*tls.Config, error) { - if h.cfg.GetProxyClientTLSConfig == nil { - return nil, trace.BadParameter("GetProxyClientTLSConfig function is not set") +// GetProxyClientCertificate returns the proxy client certificate. +func (h *Handler) GetProxyClientCertificate() (*tls.Certificate, error) { + if h.cfg.GetProxyClientCertificate == nil { + return nil, trace.BadParameter("GetProxyClientCertificate is not set") } - tlsConfig, err := h.cfg.GetProxyClientTLSConfig(ciphersuites) - return tlsConfig, trace.Wrap(err) + tlsCert, err := h.cfg.GetProxyClientCertificate() + if err != nil { + return nil, trace.Wrap(err) + } + return tlsCert, nil } // GetAccessPoint returns the caching access point. @@ -2743,6 +2752,21 @@ func calculateDesktopLogins(loginGetter loginGetter, r types.ResourceWithLabels, return logins, trace.Wrap(err) } +// calculateAppLogins determines the app logins allowed for the provided +// resource. +// +// TODO(gabrielcorado): DELETE IN V18.0.0 +// This is here for backward compatibility in case the auth server +// does not support enriched resources yet. +func calculateAppLogins(loginGetter loginGetter, r types.AppServer, allowedLogins []string) ([]string, error) { + if len(allowedLogins) > 0 { + return allowedLogins, nil + } + + logins, err := loginGetter.GetAllowedLoginsForResource(r.GetApp()) + return logins, trace.Wrap(err) +} + // getUserGroupLookup is a generator to retrieve UserGroupLookup on first call and return it again in subsequent calls. // If we encounter an error, we log it once and return an empty UserGroupLookup for the current and subsequent calls. // The returned function is not thread safe. @@ -2826,7 +2850,7 @@ func (h *Handler) clusterUnifiedResourcesGet(w http.ResponseWriter, request *htt db := ui.MakeDatabase(r.GetDatabase(), dbUsers, dbNames, enriched.RequiresRequest) unifiedResources = append(unifiedResources, db) case types.AppServer: - allowedAWSRoles, err := accessChecker.GetAllowedLoginsForResource(r.GetApp()) + allowedAWSRoles, err := calculateAppLogins(accessChecker, r, enriched.Logins) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/web/apiserver_test.go b/lib/web/apiserver_test.go index 5d6a2d1d5f924..0448541253976 100644 --- a/lib/web/apiserver_test.go +++ b/lib/web/apiserver_test.go @@ -391,9 +391,11 @@ func newWebSuiteWithConfig(t *testing.T, cfg webSuiteConfig) *WebSuite { defer caWatcher.Close() revTunServer, err := reversetunnel.NewServer(reversetunnel.Config{ - ID: node.ID(), - Listener: revTunListener, - ClientTLS: s.proxyClient.TLSConfig(), + ID: node.ID(), + Listener: revTunListener, + GetClientTLSCertificate: func() (*tls.Certificate, error) { + return &s.proxyClient.TLSConfig().Certificates[0], nil + }, ClusterName: s.server.ClusterName(), GetHostSigners: sshutils.StaticHostSigners(signer), LocalAuthClient: s.proxyClient, @@ -468,6 +470,8 @@ func newWebSuiteWithConfig(t *testing.T, cfg webSuiteConfig) *WebSuite { dns := []string{"localhost", "127.0.0.1"} proxyIdentity, err := auth.LocalRegister(authID, s.server.Auth(), nil, dns, "", nil) require.NoError(t, err) + proxyClientCert, err := keys.X509KeyPair(proxyIdentity.TLSCertBytes, proxyIdentity.KeyBytes) + require.NoError(t, err) handlerConfig := Config{ ClusterFeatures: features, @@ -488,12 +492,14 @@ func newWebSuiteWithConfig(t *testing.T, cfg webSuiteConfig) *WebSuite { ctx, err := controller(ctx, sctx, login, localAddr, remoteAddr) return ctx, trace.Wrap(err) }), - Router: router, - HealthCheckAppServer: cfg.HealthCheckAppServer, - UI: cfg.uiConfig, - PresenceChecker: cfg.presenceChecker, - GetProxyClientTLSConfig: proxyIdentity.TLSConfig, - IntegrationAppHandler: &mockIntegrationAppHandler{}, + Router: router, + HealthCheckAppServer: cfg.HealthCheckAppServer, + UI: cfg.uiConfig, + PresenceChecker: cfg.presenceChecker, + GetProxyClientCertificate: func() (*tls.Certificate, error) { + return &proxyClientCert, nil + }, + IntegrationAppHandler: &mockIntegrationAppHandler{}, } if handlerConfig.HealthCheckAppServer == nil { @@ -7994,9 +8000,11 @@ func createProxy(ctx context.Context, t *testing.T, proxyID string, node *regula t.Cleanup(proxyNodeWatcher.Close) revTunServer, err := reversetunnel.NewServer(reversetunnel.Config{ - ID: node.ID(), - Listener: revTunListener, - ClientTLS: authClient.TLSConfig(), + ID: node.ID(), + Listener: revTunListener, + GetClientTLSCertificate: func() (*tls.Certificate, error) { + return &authClient.TLSConfig().Certificates[0], nil + }, ClusterName: authServer.ClusterName(), GetHostSigners: sshutils.StaticHostSigners(hostSigners...), LocalAuthClient: client, @@ -8162,6 +8170,8 @@ func createProxy(ctx context.Context, t *testing.T, proxyID string, node *regula dns := []string{"localhost", "127.0.0.1"} proxyIdentity, err := auth.LocalRegister(authID, authServer.Auth(), nil, dns, "", nil) require.NoError(t, err) + proxyClientCert, err := keys.X509KeyPair(proxyIdentity.TLSCertBytes, proxyIdentity.KeyBytes) + require.NoError(t, err) handler, err := NewHandler(Config{ Proxy: revTunServer, AuthServers: utils.FromAddr(authServer.Addr()), @@ -8183,8 +8193,10 @@ func createProxy(ctx context.Context, t *testing.T, proxyID string, node *regula Router: router, HealthCheckAppServer: func(context.Context, string, string) error { return nil }, MinimalReverseTunnelRoutesOnly: cfg.minimalHandler, - GetProxyClientTLSConfig: proxyIdentity.TLSConfig, - IntegrationAppHandler: &mockIntegrationAppHandler{}, + GetProxyClientCertificate: func() (*tls.Certificate, error) { + return &proxyClientCert, nil + }, + IntegrationAppHandler: &mockIntegrationAppHandler{}, }, SetSessionStreamPollPeriod(200*time.Millisecond), SetClock(clock)) require.NoError(t, err) @@ -8913,8 +8925,15 @@ func startKubeWithoutCleanup(ctx context.Context, t *testing.T, cfg startKubeOpt CheckImpersonationPermissions: func(ctx context.Context, clusterName string, sarClient authztypes.SelfSubjectAccessReviewInterface) error { return nil }, - ConnTLSConfig: tlsConfig, - Clock: clockwork.NewRealClock(), + + GetConnTLSCertificate: func() (*tls.Certificate, error) { + return &tlsConfig.Certificates[0], nil + }, + GetConnTLSRoots: func() (*x509.CertPool, error) { + return tlsConfig.RootCAs, nil + }, + + Clock: clockwork.NewRealClock(), ClusterFeatures: func() authproto.Features { return authproto.Features{ Entitlements: map[string]*authproto.EntitlementInfo{ @@ -8923,7 +8942,7 @@ func startKubeWithoutCleanup(ctx context.Context, t *testing.T, cfg startKubeOpt } }, }, - TLS: tlsConfig, + TLS: tlsConfig.Clone(), AccessPoint: client, DynamicLabels: nil, LimiterConfig: limiter.Config{ @@ -10329,6 +10348,47 @@ func TestCalculateDesktopLogins(t *testing.T) { } } +func TestCalculateAppLogins(t *testing.T) { + cases := []struct { + name string + allowedLogins []string + expectedLogins []string + loginGetter loginGetterFunc + }{ + { + name: "allowed logins", + allowedLogins: []string{"llama", "fish", "dog"}, + expectedLogins: []string{"llama", "fish", "dog"}, + loginGetter: func(_ services.AccessCheckable) ([]string, error) { + return nil, nil + }, + }, + { + name: "no allowed logins", + loginGetter: func(_ services.AccessCheckable) ([]string, error) { + return nil, nil + }, + }, + { + name: "no allowed logins with fallback", + expectedLogins: []string{"apple", "banana"}, + loginGetter: func(_ services.AccessCheckable) ([]string, error) { + return []string{"apple", "banana"}, nil + }, + }, + } + + for _, test := range cases { + t.Run(test.name, func(t *testing.T) { + logins, err := calculateAppLogins(test.loginGetter, &types.AppServerV3{}, test.allowedLogins) + require.NoError(t, err) + require.Empty(t, cmp.Diff(logins, test.expectedLogins, cmpopts.SortSlices(func(a, b string) bool { + return strings.Compare(a, b) < 0 + }))) + }) + } +} + type loginGetterFunc func(resource services.AccessCheckable) ([]string, error) func (f loginGetterFunc) GetAllowedLoginsForResource(resource services.AccessCheckable) ([]string, error) { diff --git a/lib/web/integrations_awsoidc.go b/lib/web/integrations_awsoidc.go index 262798544f905..cd70b482fa7ad 100644 --- a/lib/web/integrations_awsoidc.go +++ b/lib/web/integrations_awsoidc.go @@ -752,24 +752,9 @@ func awsOIDCListAllDatabases(ctx context.Context, clt authclient.ClientI, integr func awsOIDCRequiredVPCSHelper(ctx context.Context, clt authclient.ClientI, req ui.AWSOIDCRequiredVPCSRequest, fetchedRDSs []*types.DatabaseV3) (*ui.AWSOIDCRequiredVPCSResponse, error) { // Get all database services with ecs/fargate metadata label. - nextToken := "" - fetchedDbSvcs := []types.DatabaseService{} - for { - page, err := client.GetResourcePage[types.DatabaseService](ctx, clt, &proto.ListResourcesRequest{ - ResourceType: types.KindDatabaseService, - Limit: defaults.MaxIterationLimit, - StartKey: nextToken, - Labels: map[string]string{types.AWSOIDCAgentLabel: types.True}, - }) - if err != nil { - return nil, trace.Wrap(err) - } - - fetchedDbSvcs = append(fetchedDbSvcs, page.Resources...) - nextToken = page.NextKey - if len(nextToken) == 0 { - break - } + fetchedDbSvcs, err := fetchAWSOIDCDatabaseServices(ctx, clt) + if err != nil { + return nil, trace.Wrap(err) } // Construct map of VPCs and its subnets. @@ -786,29 +771,10 @@ func awsOIDCRequiredVPCSHelper(ctx context.Context, clt authclient.ClientI, req } for _, svc := range fetchedDbSvcs { - if len(svc.GetResourceMatchers()) != 1 || svc.GetResourceMatchers()[0].Labels == nil { - continue + vpcID := getDBServiceVPC(svc, req.AccountID, req.Region) + if vpcID != "" { + delete(vpcLookup, vpcID) } - - // Database services deployed by Teleport have known configurations where - // we will only define a single resource matcher. - labelMatcher := *svc.GetResourceMatchers()[0].Labels - - // We check for length 3, because we are only - // wanting/checking for 3 discovery labels. - if len(labelMatcher) != 3 { - continue - } - if slices.Compare(labelMatcher[types.DiscoveryLabelAccountID], []string{req.AccountID}) != 0 { - continue - } - if slices.Compare(labelMatcher[types.DiscoveryLabelRegion], []string{req.Region}) != 0 { - continue - } - if len(labelMatcher[types.DiscoveryLabelVPCID]) != 1 { - continue - } - delete(vpcLookup, labelMatcher[types.DiscoveryLabelVPCID][0]) } return &ui.AWSOIDCRequiredVPCSResponse{ @@ -1185,7 +1151,7 @@ func (h *Handler) accessGraphCloudSyncOIDC(w http.ResponseWriter, r *http.Reques } } -func (h *Handler) awsAccessGraphOIDCSync(w http.ResponseWriter, r *http.Request, p httprouter.Params) (any, error) { +func (h *Handler) awsAccessGraphOIDCSync(w http.ResponseWriter, r *http.Request, _ httprouter.Params) (any, error) { queryParams := r.URL.Query() role := queryParams.Get("role") if err := aws.IsValidIAMRoleName(role); err != nil { @@ -1255,3 +1221,130 @@ func (h *Handler) awsOIDCListSubnets(w http.ResponseWriter, r *http.Request, p h Subnets: subnets, }, nil } + +// awsOIDCListDatabaseVPCs returns a list of VPCs using the ListVpcs action +// of the AWS OIDC Integration, and includes a link to the ECS service if +// a database service has been deployed for each VPC. +func (h *Handler) awsOIDCListDatabaseVPCs(w http.ResponseWriter, r *http.Request, p httprouter.Params, sctx *SessionContext, site reversetunnelclient.RemoteSite) (any, error) { + ctx := r.Context() + + var req ui.AWSOIDCListVPCsRequest + if err := httplib.ReadJSON(r, &req); err != nil { + return nil, trace.Wrap(err) + } + + integrationName := p.ByName("name") + if integrationName == "" { + return nil, trace.BadParameter("an integration name is required") + } + + clt, err := sctx.GetUserClient(ctx, site) + if err != nil { + return nil, trace.Wrap(err) + } + + listResp, err := clt.IntegrationAWSOIDCClient().ListVPCs(ctx, &integrationv1.ListVPCsRequest{ + Integration: integrationName, + Region: req.Region, + NextToken: req.NextToken, + }) + if err != nil { + return nil, trace.Wrap(err) + } + + dbServices, err := fetchAWSOIDCDatabaseServices(ctx, clt) + if err != nil { + return nil, trace.Wrap(err) + } + + serviceURLByVPC, err := getServiceURLs(dbServices, req.AccountID, req.Region, h.auth.clusterName) + if err != nil { + return nil, trace.Wrap(err) + } + + vpcs := make([]ui.DatabaseEnrollmentVPC, 0, len(listResp.Vpcs)) + for _, vpc := range listResp.Vpcs { + vpcs = append(vpcs, ui.DatabaseEnrollmentVPC{ + VPC: awsoidc.VPC{ + Name: vpc.Name, + ID: vpc.Id, + }, + ECSServiceDashboardURL: serviceURLByVPC[vpc.Id], + }) + } + + return ui.AWSOIDCDatabaseVPCsResponse{ + NextToken: listResp.NextToken, + VPCs: vpcs, + }, nil +} + +func fetchAWSOIDCDatabaseServices(ctx context.Context, clt client.GetResourcesClient) ([]types.DatabaseService, error) { + // Get all database services with the AWS OIDC agent metadata label. + var nextToken string + var fetchedDbSvcs []types.DatabaseService + for { + page, err := client.GetResourcePage[types.DatabaseService](ctx, clt, &proto.ListResourcesRequest{ + ResourceType: types.KindDatabaseService, + Limit: defaults.MaxIterationLimit, + StartKey: nextToken, + Labels: map[string]string{types.AWSOIDCAgentLabel: types.True}, + }) + if err != nil { + return nil, trace.Wrap(err) + } + + fetchedDbSvcs = append(fetchedDbSvcs, page.Resources...) + nextToken = page.NextKey + if len(nextToken) == 0 { + return fetchedDbSvcs, nil + } + } +} + +// getDBServiceVPC returns the database service's VPC ID selector value if the +// database service was deployed by the AWS OIDC integration, otherwise it +// returns an empty string. +func getDBServiceVPC(svc types.DatabaseService, accountID, region string) string { + if len(svc.GetResourceMatchers()) != 1 || svc.GetResourceMatchers()[0].Labels == nil { + return "" + } + + // Database services deployed by Teleport have known configurations where + // we will only define a single resource matcher. + labelMatcher := *svc.GetResourceMatchers()[0].Labels + + // We check for length 3, because we are only + // wanting/checking for 3 discovery labels. + if len(labelMatcher) != 3 { + return "" + } + if slices.Compare(labelMatcher[types.DiscoveryLabelAccountID], []string{accountID}) != 0 { + return "" + } + if slices.Compare(labelMatcher[types.DiscoveryLabelRegion], []string{region}) != 0 { + return "" + } + if len(labelMatcher[types.DiscoveryLabelVPCID]) != 1 { + return "" + } + return labelMatcher[types.DiscoveryLabelVPCID][0] +} + +// getServiceURLs returns a map vpcID -> service URL for ECS services deployed +// by the OIDC integration in the given account and region. +func getServiceURLs(dbServices []types.DatabaseService, accountID, region, teleportClusterName string) (map[string]string, error) { + serviceURLByVPC := make(map[string]string) + for _, svc := range dbServices { + vpcID := getDBServiceVPC(svc, accountID, region) + if vpcID == "" { + continue + } + svcURL, err := awsoidc.ECSDatabaseServiceDashboardURL(region, teleportClusterName, vpcID) + if err != nil { + return nil, trace.Wrap(err) + } + serviceURLByVPC[vpcID] = svcURL + } + return serviceURLByVPC, nil +} diff --git a/lib/web/join_tokens.go b/lib/web/join_tokens.go index 6d8129dfd52f1..c5939e30953d7 100644 --- a/lib/web/join_tokens.go +++ b/lib/web/join_tokens.go @@ -147,7 +147,12 @@ type CreateTokenRequest struct { Content string `json:"content"` } -func (h *Handler) upsertTokenContent(w http.ResponseWriter, r *http.Request, params httprouter.Params, sctx *SessionContext) (interface{}, error) { +func (h *Handler) updateTokenYAML(w http.ResponseWriter, r *http.Request, params httprouter.Params, sctx *SessionContext) (interface{}, error) { + tokenId := r.Header.Get(HeaderTokenName) + if tokenId == "" { + return nil, trace.BadParameter("requires a token name to edit") + } + var yaml CreateTokenRequest if err := httplib.ReadJSON(r, &yaml); err != nil { return nil, trace.Wrap(err) @@ -158,6 +163,10 @@ func (h *Handler) upsertTokenContent(w http.ResponseWriter, r *http.Request, par return nil, trace.Wrap(err) } + if tokenId != extractedRes.Metadata.Name { + return nil, trace.BadParameter("renaming tokens is not supported") + } + token, err := services.UnmarshalProvisionToken(extractedRes.Raw) if err != nil { return nil, trace.Wrap(err) @@ -182,7 +191,69 @@ func (h *Handler) upsertTokenContent(w http.ResponseWriter, r *http.Request, par } -func (h *Handler) createTokenHandle(w http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) { +type upsertTokenHandleRequest struct { + types.ProvisionTokenSpecV2 + Name string `json:"name"` +} + +func (h *Handler) upsertTokenHandle(w http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) { + // if using the PUT route, tokenId will be present + // in the X-Teleport-TokenName header + editing := r.Method == "PUT" + tokenId := r.Header.Get(HeaderTokenName) + if editing && tokenId == "" { + return nil, trace.BadParameter("requires a token name to edit") + } + + var req upsertTokenHandleRequest + if err := httplib.ReadJSON(r, &req); err != nil { + return nil, trace.Wrap(err) + } + + if editing && tokenId != req.Name { + return nil, trace.BadParameter("renaming tokens is not supported") + } + + // set expires time to default node join token TTL + expires := time.Now().UTC().Add(defaults.NodeJoinTokenTTL) + // IAM and GCP tokens should never expire + if req.JoinMethod == types.JoinMethodGCP || req.JoinMethod == types.JoinMethodIAM { + expires = time.Now().UTC().AddDate(1000, 0, 0) + } + + name := req.Name + if name == "" { + randName, err := utils.CryptoRandomHex(defaults.TokenLenBytes) + if err != nil { + return nil, trace.Wrap(err) + } + name = randName + } + + token, err := types.NewProvisionTokenFromSpec(name, expires, req.ProvisionTokenSpecV2) + if err != nil { + return nil, trace.Wrap(err) + } + + clt, err := ctx.GetClient() + if err != nil { + return nil, trace.Wrap(err) + } + + err = clt.UpsertToken(r.Context(), token) + if err != nil { + return nil, trace.Wrap(err) + } + + uiToken, err := ui.MakeJoinToken(token) + if err != nil { + return nil, trace.Wrap(err) + } + + return uiToken, nil +} + +func (h *Handler) createTokenForDiscoveryHandle(w http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) { clt, err := ctx.GetClient() if err != nil { return nil, trace.Wrap(err) diff --git a/lib/web/scripts/node-join/install.sh b/lib/web/scripts/node-join/install.sh index 899d1919d54c1..f1d82b2b3be58 100755 --- a/lib/web/scripts/node-join/install.sh +++ b/lib/web/scripts/node-join/install.sh @@ -518,6 +518,7 @@ EOF install_teleport_node_config() { log "Writing Teleport node service config to ${TELEPORT_CONFIG_PATH}" ${TELEPORT_BINARY_DIR}/teleport node configure \ + --silent \ --token ${JOIN_TOKEN} \ ${JOIN_METHOD_FLAG} \ --ca-pin ${CA_PINS} \ diff --git a/lib/web/ui/integration.go b/lib/web/ui/integration.go index f8f00c4142519..3de8c7bbce860 100644 --- a/lib/web/ui/integration.go +++ b/lib/web/ui/integration.go @@ -379,6 +379,39 @@ type AWSOIDCListSubnetsResponse struct { NextToken string `json:"nextToken,omitempty"` } +// AWSOIDCRequiredVPCSRequest is a request to list VPCs. +type AWSOIDCListVPCsRequest struct { + // Region is the AWS Region. + Region string `json:"region"` + // AccountID is the AWS Account ID. + AccountID string `json:"accountId"` + // NextToken is the token to be used to fetch the next page. + // If empty, the first page is fetched. + NextToken string `json:"nextToken"` +} + +// DatabaseEnrollmentVPC is a wrapper around [awsoidc.VPC] that also includes +// a link to the ECS service for a deployed Teleport database service in that +// VPC, if one exists. +type DatabaseEnrollmentVPC struct { + awsoidc.VPC + // ECSServiceDashboardURL is a link to the ECS service deployed for this + // VPC, if one exists. Can be empty. + ECSServiceDashboardURL string `json:"ecsServiceDashboardURL"` +} + +// AWSOIDCDatabaseVPCsResponse contains a list of VPCs, including a link to +// an existing db service deployment if one exists, and a next token if more +// pages are available. +type AWSOIDCDatabaseVPCsResponse struct { + // VPCs contains a page of VPCs. + VPCs []DatabaseEnrollmentVPC `json:"vpcs"` + + // NextToken is used for pagination. + // If non-empty, it can be used to request the next page. + NextToken string `json:"nextToken,omitempty"` +} + // AWSOIDCRequiredVPCSRequest is a request to get required (missing) VPC's and its subnets. type AWSOIDCRequiredVPCSRequest struct { // Region is the AWS Region. diff --git a/lib/web/ui/join_token.go b/lib/web/ui/join_token.go index f22994ed94bde..be068482aa1ba 100644 --- a/lib/web/ui/join_token.go +++ b/lib/web/ui/join_token.go @@ -32,6 +32,8 @@ type JoinToken struct { // SafeName returns the name of the token, sanitized appropriately for // join methods where the name is secret. SafeName string `json:"safeName"` + // BotName is the name of the bot this token grants access to, if any + BotName string `json:"bot_name"` // Expiry is the time that the token resource expires. Tokens that do not expire // should expect a zero value time to be returned. Expiry time.Time `json:"expiry"` @@ -41,8 +43,10 @@ type JoinToken struct { IsStatic bool `json:"isStatic"` // Method is the join method that the token supports Method types.JoinMethod `json:"method"` - // AllowRules is a list of allow rules - AllowRules []string `json:"allowRules,omitempty"` + // Allow is a list of allow rules + Allow []*types.TokenRule `json:"allow,omitempty"` + // GCP allows the configuration of options specific to the "gcp" join method. + GCP *types.ProvisionTokenSpecV2GCP `json:"gcp,omitempty"` // Content is resource yaml content. Content string `json:"content"` } @@ -52,15 +56,22 @@ func MakeJoinToken(token types.ProvisionToken) (*JoinToken, error) { if err != nil { return nil, trace.Wrap(err) } - return &JoinToken{ + uiToken := &JoinToken{ ID: token.GetName(), SafeName: token.GetSafeName(), + BotName: token.GetBotName(), Expiry: token.Expiry(), Roles: token.GetRoles(), IsStatic: token.IsStatic(), Method: token.GetJoinMethod(), + Allow: token.GetAllowRules(), Content: string(content[:]), - }, nil + } + + if uiToken.Method == types.JoinMethodGCP { + uiToken.GCP = token.GetGCPRules() + } + return uiToken, nil } func MakeJoinTokens(tokens []types.ProvisionToken) (joinTokens []JoinToken, err error) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a29b18f2aafea..4a376a5637e57 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -372,27 +372,27 @@ importers: '@opentelemetry/semantic-conventions': specifier: 1.25.1 version: 1.25.1 + '@xterm/addon-canvas': + specifier: ^0.7.0 + version: 0.7.0(@xterm/xterm@5.5.0) + '@xterm/addon-fit': + specifier: ^0.10.0 + version: 0.10.0(@xterm/xterm@5.5.0) + '@xterm/addon-web-links': + specifier: ^0.11.0 + version: 0.11.0(@xterm/xterm@5.5.0) + '@xterm/addon-webgl': + specifier: ^0.18.0 + version: 0.18.0(@xterm/xterm@5.5.0) + '@xterm/xterm': + specifier: ^5.5.0 + version: 5.5.0 create-react-class: specifier: ^15.6.3 version: 15.7.0 events: specifier: 3.3.0 version: 3.3.0 - xterm: - specifier: ^5.3.0 - version: 5.3.0 - xterm-addon-canvas: - specifier: ^0.5.0 - version: 0.5.0(xterm@5.3.0) - xterm-addon-fit: - specifier: ^0.8.0 - version: 0.8.0(xterm@5.3.0) - xterm-addon-web-links: - specifier: ^0.9.0 - version: 0.9.0(xterm@5.3.0) - xterm-addon-webgl: - specifier: ^0.16.0 - version: 0.16.0(xterm@5.3.0) devDependencies: '@gravitational/build': specifier: workspace:* @@ -470,6 +470,12 @@ importers: '@types/whatwg-url': specifier: ^11.0.5 version: 11.0.5 + '@xterm/addon-fit': + specifier: ^0.10.0 + version: 0.10.0(@xterm/xterm@5.5.0) + '@xterm/xterm': + specifier: ^5.5.0 + version: 5.5.0 electron: specifier: 31.1.0 version: 31.1.0 @@ -500,12 +506,6 @@ importers: whatwg-url: specifier: ^14.0.0 version: 14.0.0 - xterm: - specifier: ^5.3.0 - version: 5.3.0 - xterm-addon-fit: - specifier: ^0.8.0 - version: 0.8.0(xterm@5.3.0) zod: specifier: ^3.23.8 version: 3.23.8 @@ -2944,6 +2944,29 @@ packages: resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} engines: {node: '>=10.0.0'} + '@xterm/addon-canvas@0.7.0': + resolution: {integrity: sha512-LF5LYcfvefJuJ7QotNRdRSPc9YASAVDeoT5uyXS/nZshZXjYplGXRECBGiznwvhNL2I8bq1Lf5MzRwstsYQ2Iw==} + peerDependencies: + '@xterm/xterm': ^5.0.0 + + '@xterm/addon-fit@0.10.0': + resolution: {integrity: sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==} + peerDependencies: + '@xterm/xterm': ^5.0.0 + + '@xterm/addon-web-links@0.11.0': + resolution: {integrity: sha512-nIHQ38pQI+a5kXnRaTgwqSHnX7KE6+4SVoceompgHL26unAxdfP6IPqUTSYPQgSwM56hsElfoNrrW5V7BUED/Q==} + peerDependencies: + '@xterm/xterm': ^5.0.0 + + '@xterm/addon-webgl@0.18.0': + resolution: {integrity: sha512-xCnfMBTI+/HKPdRnSOHaJDRqEpq2Ugy8LEj9GiY4J3zJObo3joylIFaMvzBwbYRg8zLtkO0KQaStCeSfoaI2/w==} + peerDependencies: + '@xterm/xterm': ^5.0.0 + + '@xterm/xterm@5.5.0': + resolution: {integrity: sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==} + '@xtuc/ieee754@1.2.0': resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} @@ -7367,34 +7390,6 @@ packages: xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} - xterm-addon-canvas@0.5.0: - resolution: {integrity: sha512-QOo/eZCMrCleAgMimfdbaZCgmQRWOml63Ued6RwQ+UTPvQj3Av9QKx3xksmyYrDGRO/AVRXa9oNuzlYvLdmoLQ==} - deprecated: This package is now deprecated. Move to @xterm/addon-canvas instead. - peerDependencies: - xterm: ^5.0.0 - - xterm-addon-fit@0.8.0: - resolution: {integrity: sha512-yj3Np7XlvxxhYF/EJ7p3KHaMt6OdwQ+HDu573Vx1lRXsVxOcnVJs51RgjZOouIZOczTsskaS+CpXspK81/DLqw==} - deprecated: This package is now deprecated. Move to @xterm/addon-fit instead. - peerDependencies: - xterm: ^5.0.0 - - xterm-addon-web-links@0.9.0: - resolution: {integrity: sha512-LIzi4jBbPlrKMZF3ihoyqayWyTXAwGfu4yprz1aK2p71e9UKXN6RRzVONR0L+Zd+Ik5tPVI9bwp9e8fDTQh49Q==} - deprecated: This package is now deprecated. Move to @xterm/addon-web-links instead. - peerDependencies: - xterm: ^5.0.0 - - xterm-addon-webgl@0.16.0: - resolution: {integrity: sha512-E8cq1AiqNOv0M/FghPT+zPAEnvIQRDbAbkb04rRYSxUym69elPWVJ4sv22FCLBqM/3LcrmBLl/pELnBebVFKgA==} - deprecated: This package is now deprecated. Move to @xterm/addon-webgl instead. - peerDependencies: - xterm: ^5.0.0 - - xterm@5.3.0: - resolution: {integrity: sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg==} - deprecated: This package is now deprecated. Move to @xterm/xterm instead. - y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -10738,6 +10733,24 @@ snapshots: '@xmldom/xmldom@0.8.10': {} + '@xterm/addon-canvas@0.7.0(@xterm/xterm@5.5.0)': + dependencies: + '@xterm/xterm': 5.5.0 + + '@xterm/addon-fit@0.10.0(@xterm/xterm@5.5.0)': + dependencies: + '@xterm/xterm': 5.5.0 + + '@xterm/addon-web-links@0.11.0(@xterm/xterm@5.5.0)': + dependencies: + '@xterm/xterm': 5.5.0 + + '@xterm/addon-webgl@0.18.0(@xterm/xterm@5.5.0)': + dependencies: + '@xterm/xterm': 5.5.0 + + '@xterm/xterm@5.5.0': {} + '@xtuc/ieee754@1.2.0': {} '@xtuc/long@4.2.2': {} @@ -16132,24 +16145,6 @@ snapshots: xmlchars@2.2.0: {} - xterm-addon-canvas@0.5.0(xterm@5.3.0): - dependencies: - xterm: 5.3.0 - - xterm-addon-fit@0.8.0(xterm@5.3.0): - dependencies: - xterm: 5.3.0 - - xterm-addon-web-links@0.9.0(xterm@5.3.0): - dependencies: - xterm: 5.3.0 - - xterm-addon-webgl@0.16.0(xterm@5.3.0): - dependencies: - xterm: 5.3.0 - - xterm@5.3.0: {} - y18n@5.0.8: {} yallist@3.1.1: {} diff --git a/tool/teleport/common/integration_configure.go b/tool/teleport/common/integration_configure.go index 27d2134e0bcf7..dab36f0a5f8b3 100644 --- a/tool/teleport/common/integration_configure.go +++ b/tool/teleport/common/integration_configure.go @@ -144,8 +144,6 @@ func onIntegrationConfAWSOIDCIdP(ctx context.Context, clf config.CommandLineFlag IntegrationName: clf.IntegrationConfAWSOIDCIdPArguments.Name, IntegrationRole: clf.IntegrationConfAWSOIDCIdPArguments.Role, ProxyPublicAddress: clf.IntegrationConfAWSOIDCIdPArguments.ProxyPublicURL, - S3BucketLocation: clf.IntegrationConfAWSOIDCIdPArguments.S3BucketURI, - S3JWKSContentsB64: clf.IntegrationConfAWSOIDCIdPArguments.S3JWKSContentsB64, } return trace.Wrap(awsoidc.ConfigureIdPIAM(ctx, iamClient, confReq)) } diff --git a/tool/teleport/common/teleport.go b/tool/teleport/common/teleport.go index f5ba91e7b5e88..02f846853e697 100644 --- a/tool/teleport/common/teleport.go +++ b/tool/teleport/common/teleport.go @@ -299,7 +299,7 @@ func Run(options Options) (app *kingpin.Application, executedCommand string, con dbConfigureCreate.Flag("gcp-instance-id", "(Only for Cloud SQL) GCP Cloud SQL instance identifier.").StringVar(&dbConfigCreateFlags.DatabaseGCPInstanceID) dbConfigureCreate.Flag("ca-cert-file", "Database CA certificate path.").StringVar(&dbConfigCreateFlags.DatabaseCACertFile) dbConfigureCreate.Flag("output", - "Write to stdout with -o=stdout, default config file with -o=file or custom path with -o=file:///path").Short('o').Default( + `Write to stdout with "--output=stdout", default config file with "--output=file" or custom path with --output=file:///path`).Short('o').Default( teleport.SchemeStdout).StringVar(&dbConfigCreateFlags.output) dbConfigureCreate.Flag("dynamic-resources-labels", "Comma-separated list(s) of labels to match dynamic resources, for example env=dev,dept=it. Required to enable dynamic resources matching.").StringsVar(&dbConfigCreateFlags.DynamicResourcesRawLabels) dbConfigureCreate.Flag("trust-system-cert-pool", "Allows Teleport to trust certificate authorities available on the host system for self-hosted databases.").BoolVar(&dbConfigCreateFlags.DatabaseTrustSystemCertPool) @@ -367,7 +367,7 @@ func Run(options Options) (app *kingpin.Application, executedCommand string, con systemdInstall.Flag("pid-file", "Full path to the PID file.").Default(config.SystemdDefaultPIDFile).StringVar(&systemdInstallFlags.PIDFile) systemdInstall.Flag("fd-limit", "Maximum number of open file descriptors.").Default(fmt.Sprintf("%v", config.SystemdDefaultFileDescriptorLimit)).IntVar(&systemdInstallFlags.FileDescriptorLimit) systemdInstall.Flag("teleport-path", "Full path to the Teleport binary.").StringVar(&systemdInstallFlags.TeleportInstallationFile) - systemdInstall.Flag("output", "Write to stdout with -o=stdout or custom path with -o=file:///path").Short('o').Default(teleport.SchemeStdout).StringVar(&systemdInstallFlags.output) + systemdInstall.Flag("output", `Write to stdout with "--output=stdout" or custom path with --output=file:///path`).Short('o').Default(teleport.SchemeStdout).StringVar(&systemdInstallFlags.output) systemdInstall.Alias(systemdInstallExamples) // We're using "alias" section to display usage examples. // This command is hidden because it is only meant to be used by the AutoDiscover script. @@ -395,7 +395,7 @@ func Run(options Options) (app *kingpin.Application, executedCommand string, con dump.Flag("cluster-name", "Unique cluster name, e.g. example.com.").StringVar(&dumpFlags.ClusterName) dump.Flag("output", - "Write to stdout with -o=stdout, default config file with -o=file or custom path with -o=file:///path").Short('o').Default( + `Write to stdout with "--output=stdout", default config file with "--output=file" or custom path with --output=file:///path`).Short('o').Default( teleport.SchemeStdout).StringVar(&dumpFlags.output) dump.Flag("acme", "Get automatic certificate from Letsencrypt.org using ACME.").BoolVar(&dumpFlags.ACMEEnabled) @@ -423,7 +423,7 @@ func Run(options Options) (app *kingpin.Application, executedCommand string, con dumpNodeConfigure.Flag("cluster-name", "Unique cluster name, e.g. example.com.").StringVar(&dumpFlags.ClusterName) dumpNodeConfigure.Flag("output", - "Write to stdout with -o=stdout, default config file with -o=file or custom path with -o=file:///path").Short('o').Default( + `Write to stdout with "--output=stdout", default config file with "--output=file" or custom path with --output=file:///path`).Short('o').Default( teleport.SchemeStdout).StringVar(&dumpFlags.output) dumpNodeConfigure.Flag("version", "Teleport configuration version.").Default(defaults.TeleportConfigVersionV3).StringVar(&dumpFlags.Version) dumpNodeConfigure.Flag("public-addr", "The hostport that the node advertises for the SSH endpoint.").StringVar(&dumpFlags.PublicAddr) @@ -503,13 +503,9 @@ func Run(options Options) (app *kingpin.Application, executedCommand string, con IntegrationConfAWSOIDCIdPArguments.Name) integrationConfAWSOIDCIdPCmd.Flag("role", "The AWS Role used by the AWS OIDC Integration.").Required().StringVar(&ccf. IntegrationConfAWSOIDCIdPArguments.Role) - integrationConfAWSOIDCIdPCmd.Flag("proxy-public-url", "Proxy Public URL (eg https://mytenant.teleport.sh).").StringVar(&ccf. + integrationConfAWSOIDCIdPCmd.Flag("proxy-public-url", "Proxy Public URL (eg https://mytenant.teleport.sh).").Required().StringVar(&ccf. IntegrationConfAWSOIDCIdPArguments.ProxyPublicURL) integrationConfAWSOIDCIdPCmd.Flag("insecure", "Insecure mode disables certificate validation.").BoolVar(&ccf.InsecureMode) - integrationConfAWSOIDCIdPCmd.Flag("s3-bucket-uri", "The S3 URI(format: s3:///) used to store the OpenID configuration and public keys. ").StringVar(&ccf. - IntegrationConfAWSOIDCIdPArguments.S3BucketURI) - integrationConfAWSOIDCIdPCmd.Flag("s3-jwks-base64", `The JWKS base 64 encoded. Required when using the S3 Bucket as the Issuer URL. Format: base64({"keys":[{"kty":"RSA","alg":"RS256","n":"","e":"","use":"sig","kid":""}]}).`).StringVar(&ccf. - IntegrationConfAWSOIDCIdPArguments.S3JWKSContentsB64) integrationConfListDatabasesCmd := integrationConfigureCmd.Command("listdatabases-iam", "Adds required IAM permissions to List RDS Databases (Instances and Clusters).") integrationConfListDatabasesCmd.Flag("aws-region", "AWS Region.").Required().StringVar(&ccf.IntegrationConfListDatabasesIAMArguments.Region) @@ -919,7 +915,7 @@ func onConfigDump(flags dumpFlags) error { fmt.Fprintf(flags.stdout, "- The Teleport configuration is located at %q.\n", configPath) } if !canWriteToDataDir { - fmt.Fprintf(flags.stdout, "- Teleport will be storing data at %q. To change that, run \"teleport configure\" with the \"--data-dir\" flag.\n", flags.DataDir) + fmt.Fprintf(flags.stdout, "- Teleport will be storing data at %q. To change that, edit the \"data_dir\" field in %q.", flags.DataDir, configPath) } fmt.Fprintf(flags.stdout, "\n") } else { diff --git a/tool/tsh/common/scan.go b/tool/tsh/common/scan.go new file mode 100644 index 0000000000000..f96eff417b468 --- /dev/null +++ b/tool/tsh/common/scan.go @@ -0,0 +1,170 @@ +/* + * 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 common + +import ( + "fmt" + "log/slog" + "runtime" + "strings" + + "github.com/alecthomas/kingpin/v2" + "github.com/gravitational/trace" + + "github.com/gravitational/teleport/api/constants" + accessgraphsecretsv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/accessgraph/v1" + "github.com/gravitational/teleport/api/types/accessgraph" + "github.com/gravitational/teleport/lib/devicetrust/assert" + dtnative "github.com/gravitational/teleport/lib/devicetrust/native" + secretsscannerclient "github.com/gravitational/teleport/lib/secretsscanner/client" + secretsreporter "github.com/gravitational/teleport/lib/secretsscanner/reporter" + secretsscanner "github.com/gravitational/teleport/lib/secretsscanner/scaner" +) + +type scanCommand struct { + keys *scanKeysCommand +} + +func newScanCommand(app *kingpin.Application) scanCommand { + scan := app.Command("scan", "Scan the local machine for Secrets and report findings to Teleport") + cmd := scanCommand{ + keys: newScanKeysCommand(scan), + } + return cmd +} + +type scanKeysCommand struct { + *kingpin.CmdClause + dirs []string + skipPaths []string +} + +func newScanKeysCommand(parent *kingpin.CmdClause) *scanKeysCommand { + c := &scanKeysCommand{CmdClause: parent.Command("keys", "Scan the local machine for SSH private keys and report findings to Teleport")} + c.Flag("dirs", "Directories to scan.").Default(defaultDirValues()).StringsVar(&c.dirs) + c.Flag("skip-paths", "Paths to directories or files to skip. Supports for matching patterns.").StringsVar(&c.skipPaths) + return c +} + +func defaultDirValues() string { + switch runtime.GOOS { + case constants.LinuxOS: + return "/home/" + case constants.DarwinOS: + return "/Users/" + case constants.WindowsOS: + return "C:\\Users\\" + default: + return "/" + } +} + +func (c *scanKeysCommand) run(cf *CLIConf) error { + if len(c.dirs) == 0 { + return trace.BadParameter("no directories to scan") + } + + if cf.Proxy == "" { + return trace.BadParameter("proxy address is required") + } + + ctx := cf.Context + + deviceCred, err := dtnative.GetDeviceCredential() + if err != nil { + return trace.Wrap(err, "device not enrolled") + } + + fmt.Printf("Device trust credentials found.\nScanning %s.\n", strings.Join(c.dirs, ", ")) + + scanner, err := secretsscanner.New(secretsscanner.Config{ + Dirs: c.dirs, + SkipPaths: c.skipPaths, + Log: slog.Default(), + }) + if err != nil { + return trace.Wrap(err, "failed to create scanner") + } + + privateKeys := scanner.ScanPrivateKeys( + ctx, + deviceCred.Id, + ) + + printPrivateKeys(privateKeys) + + client, err := secretsscannerclient.NewSecretsScannerServiceClient( + ctx, + secretsscannerclient.ClientConfig{ + ProxyServer: cf.Proxy, + Insecure: cf.InsecureSkipVerify, + Log: slog.Default(), + }) + if err != nil { + return trace.Wrap(err, "failed to create client") + } + + reporter, err := secretsreporter.New( + secretsreporter.Config{ + Client: client, + Log: slog.Default(), + AssertCeremonyBuilder: func() (*assert.Ceremony, error) { + return assert.NewCeremony() + }, + }, + ) + if err != nil { + return trace.Wrap(err, "failed to create reporter") + } + + if err := reporter.ReportPrivateKeys(ctx, collectPrivateKeys(privateKeys)); trace.IsNotImplemented(err) { + return handleUnimplementedError(ctx, err, *cf) + } else if err != nil { + return trace.Wrap(err, "failed to report private keys") + } + + fmt.Printf("Reported %d SSH fingerprints to Teleport.\n", len(privateKeys)) + + return nil +} + +func printPrivateKeys(privateKeys []secretsscanner.SSHPrivateKey) { + if len(privateKeys) == 0 { + fmt.Println("No SSH private keys found.") + return + } + + fmt.Println("SSH private keys found:") + for _, pk := range privateKeys { + path, key := pk.Path, pk.Key + fmt.Printf("- SHA256 fingerprint: %q (mode: %s) at %s\n", + key.Spec.PublicKeyFingerprint, + accessgraph.DescribePublicKeyMode(key.Spec.PublicKeyMode), + path, + ) + } +} + +func collectPrivateKeys(privateKeys []secretsscanner.SSHPrivateKey) []*accessgraphsecretsv1pb.PrivateKey { + keys := make([]*accessgraphsecretsv1pb.PrivateKey, 0, len(privateKeys)) + for _, pk := range privateKeys { + keys = append(keys, pk.Key) + } + return keys +} diff --git a/tool/tsh/common/tsh.go b/tool/tsh/common/tsh.go index b88d502d84b10..215b043c6c732 100644 --- a/tool/tsh/common/tsh.go +++ b/tool/tsh/common/tsh.go @@ -1173,6 +1173,8 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error { kube := newKubeCommand(app) // MFA subcommands. mfa := newMFACommand(app) + // SCAN subcommands. + scan := newScanCommand(app) config := app.Command("config", "Print OpenSSH configuration details.") config.Flag("port", "SSH port on a remote host").Short('p').Int32Var(&cf.NodePort) @@ -1471,7 +1473,8 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error { err = kube.exec.run(&cf) case kube.join.FullCommand(): err = kube.join.run(&cf) - + case scan.keys.FullCommand(): + err = scan.keys.run(&cf) case proxySSH.FullCommand(): err = onProxyCommandSSH(&cf) case proxyDB.FullCommand(): diff --git a/web/.storybook/public/mockServiceWorker.js b/web/.storybook/public/mockServiceWorker.js index ae6e8bade2777..29137906cdda3 100644 --- a/web/.storybook/public/mockServiceWorker.js +++ b/web/.storybook/public/mockServiceWorker.js @@ -8,42 +8,42 @@ * - Please do NOT serve this file on production. */ -const PACKAGE_VERSION = '2.3.2'; -const INTEGRITY_CHECKSUM = '26357c79639bfa20d64c0efca2a87423'; -const IS_MOCKED_RESPONSE = Symbol('isMockedResponse'); -const activeClientIds = new Set(); +const PACKAGE_VERSION = '2.3.2' +const INTEGRITY_CHECKSUM = '26357c79639bfa20d64c0efca2a87423' +const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') +const activeClientIds = new Set() self.addEventListener('install', function () { - self.skipWaiting(); -}); + self.skipWaiting() +}) self.addEventListener('activate', function (event) { - event.waitUntil(self.clients.claim()); -}); + event.waitUntil(self.clients.claim()) +}) self.addEventListener('message', async function (event) { - const clientId = event.source.id; + const clientId = event.source.id if (!clientId || !self.clients) { - return; + return } - const client = await self.clients.get(clientId); + const client = await self.clients.get(clientId) if (!client) { - return; + return } const allClients = await self.clients.matchAll({ type: 'window', - }); + }) switch (event.data) { case 'KEEPALIVE_REQUEST': { sendToClient(client, { type: 'KEEPALIVE_RESPONSE', - }); - break; + }) + break } case 'INTEGRITY_CHECK_REQUEST': { @@ -53,78 +53,78 @@ self.addEventListener('message', async function (event) { packageVersion: PACKAGE_VERSION, checksum: INTEGRITY_CHECKSUM, }, - }); - break; + }) + break } case 'MOCK_ACTIVATE': { - activeClientIds.add(clientId); + activeClientIds.add(clientId) sendToClient(client, { type: 'MOCKING_ENABLED', payload: true, - }); - break; + }) + break } case 'MOCK_DEACTIVATE': { - activeClientIds.delete(clientId); - break; + activeClientIds.delete(clientId) + break } case 'CLIENT_CLOSED': { - activeClientIds.delete(clientId); + activeClientIds.delete(clientId) - const remainingClients = allClients.filter(client => { - return client.id !== clientId; - }); + const remainingClients = allClients.filter((client) => { + return client.id !== clientId + }) // Unregister itself when there are no more clients if (remainingClients.length === 0) { - self.registration.unregister(); + self.registration.unregister() } - break; + break } } -}); +}) self.addEventListener('fetch', function (event) { - const { request } = event; + const { request } = event // Bypass navigation requests. if (request.mode === 'navigate') { - return; + return } // Opening the DevTools triggers the "only-if-cached" request // that cannot be handled by the worker. Bypass such requests. if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { - return; + return } // Bypass all requests when there are no active clients. // Prevents the self-unregistered worked from handling requests // after it's been deleted (still remains active until the next reload). if (activeClientIds.size === 0) { - return; + return } // Generate unique request ID. - const requestId = crypto.randomUUID(); - event.respondWith(handleRequest(event, requestId)); -}); + const requestId = crypto.randomUUID() + event.respondWith(handleRequest(event, requestId)) +}) async function handleRequest(event, requestId) { - const client = await resolveMainClient(event); - const response = await getResponse(event, client, requestId); + const client = await resolveMainClient(event) + const response = await getResponse(event, client, requestId) // Send back the response clone for the "response:*" life-cycle events. // Ensure MSW is active and ready to handle the message, otherwise // this message will pend indefinitely. if (client && activeClientIds.has(client.id)) { - (async function () { - const responseClone = response.clone(); + ;(async function () { + const responseClone = response.clone() sendToClient( client, @@ -140,12 +140,12 @@ async function handleRequest(event, requestId) { headers: Object.fromEntries(responseClone.headers.entries()), }, }, - [responseClone.body] - ); - })(); + [responseClone.body], + ) + })() } - return response; + return response } // Resolve the main client for the given event. @@ -153,49 +153,49 @@ async function handleRequest(event, requestId) { // that registered the worker. It's with the latter the worker should // communicate with during the response resolving phase. async function resolveMainClient(event) { - const client = await self.clients.get(event.clientId); + const client = await self.clients.get(event.clientId) if (client?.frameType === 'top-level') { - return client; + return client } const allClients = await self.clients.matchAll({ type: 'window', - }); + }) return allClients - .filter(client => { + .filter((client) => { // Get only those clients that are currently visible. - return client.visibilityState === 'visible'; + return client.visibilityState === 'visible' }) - .find(client => { + .find((client) => { // Find the client ID that's recorded in the // set of clients that have registered the worker. - return activeClientIds.has(client.id); - }); + return activeClientIds.has(client.id) + }) } async function getResponse(event, client, requestId) { - const { request } = event; + const { request } = event // Clone the request because it might've been already used // (i.e. its body has been read and sent to the client). - const requestClone = request.clone(); + const requestClone = request.clone() function passthrough() { - const headers = Object.fromEntries(requestClone.headers.entries()); + const headers = Object.fromEntries(requestClone.headers.entries()) // Remove internal MSW request header so the passthrough request // complies with any potential CORS preflight checks on the server. // Some servers forbid unknown request headers. - delete headers['x-msw-intention']; + delete headers['x-msw-intention'] - return fetch(requestClone, { headers }); + return fetch(requestClone, { headers }) } // Bypass mocking when the client is not active. if (!client) { - return passthrough(); + return passthrough() } // Bypass initial page load requests (i.e. static assets). @@ -203,11 +203,11 @@ async function getResponse(event, client, requestId) { // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet // and is not ready to handle requests. if (!activeClientIds.has(client.id)) { - return passthrough(); + return passthrough() } // Notify the client that a request has been intercepted. - const requestBuffer = await request.arrayBuffer(); + const requestBuffer = await request.arrayBuffer() const clientMessage = await sendToClient( client, { @@ -229,39 +229,39 @@ async function getResponse(event, client, requestId) { keepalive: request.keepalive, }, }, - [requestBuffer] - ); + [requestBuffer], + ) switch (clientMessage.type) { case 'MOCK_RESPONSE': { - return respondWithMock(clientMessage.data); + return respondWithMock(clientMessage.data) } case 'PASSTHROUGH': { - return passthrough(); + return passthrough() } } - return passthrough(); + return passthrough() } function sendToClient(client, message, transferrables = []) { return new Promise((resolve, reject) => { - const channel = new MessageChannel(); + const channel = new MessageChannel() - channel.port1.onmessage = event => { + channel.port1.onmessage = (event) => { if (event.data && event.data.error) { - return reject(event.data.error); + return reject(event.data.error) } - resolve(event.data); - }; + resolve(event.data) + } client.postMessage( message, - [channel.port2].concat(transferrables.filter(Boolean)) - ); - }); + [channel.port2].concat(transferrables.filter(Boolean)), + ) + }) } async function respondWithMock(response) { @@ -270,15 +270,15 @@ async function respondWithMock(response) { // instance will have status code set to 0. Since it's not possible to create // a Response instance with status code 0, handle that use-case separately. if (response.status === 0) { - return Response.error(); + return Response.error() } - const mockedResponse = new Response(response.body, response); + const mockedResponse = new Response(response.body, response) Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { value: true, enumerable: true, - }); + }) - return mockedResponse; + return mockedResponse } diff --git a/web/packages/shared/components/AuthorizeDeviceWeb/AuthorizeDeviceWeb.tsx b/web/packages/shared/components/AuthorizeDeviceWeb/AuthorizeDeviceWeb.tsx index 9a46336ea0efb..0bf8023beacd9 100644 --- a/web/packages/shared/components/AuthorizeDeviceWeb/AuthorizeDeviceWeb.tsx +++ b/web/packages/shared/components/AuthorizeDeviceWeb/AuthorizeDeviceWeb.tsx @@ -139,8 +139,10 @@ export const DeviceTrustConnectPassthrough = ({ const SkipAuthNotice = styled(Box)` text-align: center; width: 100%; - position: absolute; - bottom: 24px; + @media (min-height: 500px) { + position: absolute; + bottom: 24px; + } `; const DownloadButton = styled(ButtonLink)` @@ -156,5 +158,5 @@ const BoldText = styled.span` const Wrapper = styled(Box)` text-align: center; line-height: 32px; - padding-top: 200px; + padding-top: 5vh; `; diff --git a/web/packages/shared/components/UnifiedResources/CardsView/ResourceCard.story.tsx b/web/packages/shared/components/UnifiedResources/CardsView/ResourceCard.story.tsx index 2740e750e0b49..bf457bab6dc06 100644 --- a/web/packages/shared/components/UnifiedResources/CardsView/ResourceCard.story.tsx +++ b/web/packages/shared/components/UnifiedResources/CardsView/ResourceCard.story.tsx @@ -33,6 +33,7 @@ import { nodes } from 'teleport/Nodes/fixtures'; import makeApp from 'teleport/services/apps/makeApps'; import { ResourceActionButton } from 'teleport/UnifiedResources/ResourceActionButton'; +import { SamlAppActionProvider } from 'teleport/SamlApplications/useSamlAppActions'; import { makeUnifiedResourceViewItemApp, @@ -102,56 +103,51 @@ const ActionButton = Action; export const Cards: Story = { render() { return ( - - {[ - ...apps.map(resource => - makeUnifiedResourceViewItemApp(resource, { - ActionButton: ( - - alert('Sets resource spec and opens update dialog') - } - /> - ), - }) - ), - ...databases.map(resource => - makeUnifiedResourceViewItemDatabase(resource, { - ActionButton, - }) - ), - ...kubes.map(resource => - makeUnifiedResourceViewItemKube(resource, { ActionButton }) - ), - ...nodes.map(resource => - makeUnifiedResourceViewItemNode(resource, { - ActionButton, - }) - ), - ...additionalResources.map(resource => - makeUnifiedResourceViewItemApp(resource, { ActionButton }) - ), - ...desktops.map(resource => - makeUnifiedResourceViewItemDesktop(resource, { ActionButton }) - ), - ].map((res, i) => ( - {}} - selectResource={() => {}} - selected={false} - pinningSupport={PinningSupport.Supported} - name={res.name} - primaryIconName={res.primaryIconName} - SecondaryIcon={res.SecondaryIcon} - cardViewProps={res.cardViewProps} - labels={res.labels} - ActionButton={res.ActionButton} - /> - ))} - + + + {[ + ...apps.map(resource => + makeUnifiedResourceViewItemApp(resource, { + ActionButton: , + }) + ), + ...databases.map(resource => + makeUnifiedResourceViewItemDatabase(resource, { + ActionButton, + }) + ), + ...kubes.map(resource => + makeUnifiedResourceViewItemKube(resource, { ActionButton }) + ), + ...nodes.map(resource => + makeUnifiedResourceViewItemNode(resource, { + ActionButton, + }) + ), + ...additionalResources.map(resource => + makeUnifiedResourceViewItemApp(resource, { ActionButton }) + ), + ...desktops.map(resource => + makeUnifiedResourceViewItemDesktop(resource, { ActionButton }) + ), + ].map((res, i) => ( + {}} + selectResource={() => {}} + selected={false} + pinningSupport={PinningSupport.Supported} + name={res.name} + primaryIconName={res.primaryIconName} + SecondaryIcon={res.SecondaryIcon} + cardViewProps={res.cardViewProps} + labels={res.labels} + ActionButton={res.ActionButton} + /> + ))} + + ); }, }; diff --git a/web/packages/teleport/package.json b/web/packages/teleport/package.json index 3f4aa00aee9a1..cf7bbcafc55d9 100644 --- a/web/packages/teleport/package.json +++ b/web/packages/teleport/package.json @@ -32,13 +32,13 @@ "@opentelemetry/sdk-trace-base": "1.25.1", "@opentelemetry/sdk-trace-web": "1.25.1", "@opentelemetry/semantic-conventions": "1.25.1", + "@xterm/xterm": "^5.5.0", + "@xterm/addon-canvas": "^0.7.0", + "@xterm/addon-fit": "^0.10.0", + "@xterm/addon-web-links": "^0.11.0", + "@xterm/addon-webgl": "^0.18.0", "create-react-class": "^15.6.3", - "events": "3.3.0", - "xterm": "^5.3.0", - "xterm-addon-canvas": "^0.5.0", - "xterm-addon-fit": "^0.8.0", - "xterm-addon-web-links": "^0.9.0", - "xterm-addon-webgl": "^0.16.0" + "events": "3.3.0" }, "devDependencies": { "@gravitational/build": "workspace:*", diff --git a/web/packages/teleport/src/Console/DocumentSsh/Terminal/Terminal.tsx b/web/packages/teleport/src/Console/DocumentSsh/Terminal/Terminal.tsx index 7036779970b17..80c762089cb1e 100644 --- a/web/packages/teleport/src/Console/DocumentSsh/Terminal/Terminal.tsx +++ b/web/packages/teleport/src/Console/DocumentSsh/Terminal/Terminal.tsx @@ -23,7 +23,7 @@ import React, { useRef, } from 'react'; import { Flex } from 'design'; -import { ITheme } from 'xterm'; +import { ITheme } from '@xterm/xterm'; import { getPlatformType } from 'design/platform'; diff --git a/web/packages/teleport/src/Console/StyledXterm/StyledXterm.tsx b/web/packages/teleport/src/Console/StyledXterm/StyledXterm.tsx index 7c20e10b45356..07f0aba027f99 100644 --- a/web/packages/teleport/src/Console/StyledXterm/StyledXterm.tsx +++ b/web/packages/teleport/src/Console/StyledXterm/StyledXterm.tsx @@ -19,7 +19,7 @@ import styled from 'styled-components'; import { Box } from 'design'; -import 'xterm/css/xterm.css'; +import '@xterm/xterm/css/xterm.css'; const StyledXterm = styled(Box)( () => ` diff --git a/web/packages/teleport/src/JoinTokens/JoinTokenForms.tsx b/web/packages/teleport/src/JoinTokens/JoinTokenForms.tsx new file mode 100644 index 0000000000000..e511ded3df6bf --- /dev/null +++ b/web/packages/teleport/src/JoinTokens/JoinTokenForms.tsx @@ -0,0 +1,230 @@ +/** + * 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 . + */ + +import React from 'react'; +import { Flex, Text, ButtonIcon, ButtonText } from 'design'; +import { Plus, Trash } from 'design/Icon'; +import { requiredField } from 'shared/components/Validation/rules'; +import FieldInput from 'shared/components/FieldInput'; +import { FieldSelectCreatable } from 'shared/components/FieldSelect'; + +import { NewJoinTokenState, OptionGCP, RuleBox } from './UpsertJoinTokenDialog'; + +export const JoinTokenIAMForm = ({ + tokenState, + onUpdateState, +}: { + tokenState: NewJoinTokenState; + onUpdateState: (newToken: NewJoinTokenState) => void; +}) => { + const rules = tokenState.iam; + + function removeRule(index: number) { + const newRules = rules.filter((_, i) => index !== i); + const newState = { + ...tokenState, + iam: newRules, + }; + onUpdateState(newState); + } + + function setTokenRulesField( + ruleIndex: number, + fieldName: string, + value: string + ) { + const newState = { + ...tokenState, + [tokenState.method.value]: tokenState[tokenState.method.value].map( + (rule, i) => { + if (ruleIndex !== i) { + return rule; + } + return { + ...rule, + [fieldName]: value, + }; + } + ), + }; + onUpdateState(newState); + } + + function addNewRule() { + const newState = { + ...tokenState, + iam: [...tokenState.iam, { aws_account: '' }], + }; + onUpdateState(newState); + } + + return ( + <> + {rules.map((rule, index) => ( + + + + AWS Rule + + + {rules.length > 1 && ( // at least one rule is required, so lets not allow the user to remove it + removeRule(index)} + > + + + )} + + + setTokenRulesField(index, 'aws_account', e.target.value) + } + /> + setTokenRulesField(index, 'aws_arn', e.target.value)} + /> + + ))} + + + Add another AWS Rule + + + ); +}; + +export const JoinTokenGCPForm = ({ + tokenState, + onUpdateState, +}: { + tokenState: NewJoinTokenState; + onUpdateState: (newToken: NewJoinTokenState) => void; +}) => { + const rules = tokenState.gcp; + function removeRule(index: number) { + const newRules = rules.filter((_, i) => index !== i); + const newState = { + ...tokenState, + gcp: newRules, + }; + onUpdateState(newState); + } + + function addNewRule() { + const newState = { + ...tokenState, + gcp: [ + ...tokenState.gcp, + { project_ids: [], locations: [], service_accounts: [] }, + ], + }; + onUpdateState(newState); + } + + function updateRuleField( + index: number, + fieldName: string, + opts: OptionGCP[] + ) { + const newState = { + ...tokenState, + gcp: tokenState.gcp.map((rule, i) => { + if (i === index) { + return { ...rule, [fieldName]: opts }; + } + return rule; + }), + }; + onUpdateState(newState); + } + + return ( + <> + {rules.map((rule, index) => ( + + + + GCP Rule + + + {rules.length > 1 && ( // at least one rule is required, so lets not allow the user to remove it + removeRule(index)} + > + + + )} + + + updateRuleField(index, 'project_ids', opts as OptionGCP[]) + } + value={rule.project_ids} + label="Add Project ID(s)" + rule={requiredField('At least 1 Project ID required')} + /> + + updateRuleField(index, 'locations', opts as OptionGCP[]) + } + value={rule.locations} + label="Add Locations" + labelTip="Allows regions and/or zones." + /> + + updateRuleField(index, 'service_accounts', opts as OptionGCP[]) + } + value={rule.service_accounts} + label="Add Service Account Emails" + /> + + ))} + + + Add another GCP Rule + + + ); +}; diff --git a/web/packages/teleport/src/JoinTokens/JoinTokens.story.tsx b/web/packages/teleport/src/JoinTokens/JoinTokens.story.tsx index 453abc753dd54..5e80cb99b80e3 100644 --- a/web/packages/teleport/src/JoinTokens/JoinTokens.story.tsx +++ b/web/packages/teleport/src/JoinTokens/JoinTokens.story.tsx @@ -68,6 +68,10 @@ const tokens: JoinToken[] = [ expiry: new Date('0001-01-01'), method: 'token', safeName: '******', + allow: [], + gcp: { + allow: [], + }, content: '', }, { @@ -77,6 +81,10 @@ const tokens: JoinToken[] = [ expiry: new Date('2023-06-01'), method: 'iam', safeName: 'iam-EDIT-ME-BUT-DONT-SAVE', + allow: [], + gcp: { + allow: [], + }, content: `kind: token metadata: name: iam-EDIT-ME-BUT-DONT-SAVE diff --git a/web/packages/teleport/src/JoinTokens/JoinTokens.test.tsx b/web/packages/teleport/src/JoinTokens/JoinTokens.test.tsx new file mode 100644 index 0000000000000..8a14145e8303a --- /dev/null +++ b/web/packages/teleport/src/JoinTokens/JoinTokens.test.tsx @@ -0,0 +1,191 @@ +/** + * Teleport + * Copyright (C) 2023 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 . + */ +import { render, screen, fireEvent } from 'design/utils/testing'; +import userEvent from '@testing-library/user-event'; +import { within } from '@testing-library/react'; + +import { createTeleportContext } from 'teleport/mocks/contexts'; +import { ContextProvider } from 'teleport'; +import makeJoinToken from 'teleport/services/joinToken/makeJoinToken'; + +import { JoinTokens } from './JoinTokens'; + +describe('JoinTokens', () => { + test('create dialog opens', async () => { + render(); + await userEvent.click( + screen.getByRole('button', { name: /create new token/i }) + ); + + expect(screen.getByText(/create a new join token/i)).toBeInTheDocument(); + }); + + test('edit dialog opens with values', async () => { + const token = tokens[0]; + render(); + const optionButtons = await screen.findAllByText(/options/i); + await userEvent.click(optionButtons[0]); + const editButtons = await screen.findAllByText(/view\/edit/i); + await userEvent.click(editButtons[0]); + expect(screen.getByText(/edit token/i)).toBeInTheDocument(); + + expect(screen.getByDisplayValue(token.id)).toBeInTheDocument(); + expect( + screen.getByDisplayValue(token.allow[0].aws_account) + ).toBeInTheDocument(); + }); + + test('create form fails if roles arent selected', async () => { + render(); + await userEvent.click( + screen.getByRole('button', { name: /create new token/i }) + ); + + fireEvent.change(screen.getByPlaceholderText('iam-token-name'), { + target: { value: 'the_token' }, + }); + + fireEvent.click(screen.getByRole('button', { name: /create join token/i })); + expect( + screen.getByText('At least one role is required') + ).toBeInTheDocument(); + }); + + test('successful create adds token to the table', async () => { + render(); + await userEvent.click( + screen.getByRole('button', { name: /create new token/i }) + ); + + fireEvent.change(screen.getByPlaceholderText('iam-token-name'), { + target: { value: 'the_token' }, + }); + + const inputEl = within(screen.getByTestId('role_select')).getByRole( + 'textbox' + ); + fireEvent.change(inputEl, { target: { value: 'Node' } }); + fireEvent.focus(inputEl); + fireEvent.keyDown(inputEl, { key: 'Enter', keyCode: 13 }); + + fireEvent.click(screen.getByRole('button', { name: /create join token/i })); + expect( + screen.queryByText('At least one role is required') + ).not.toBeInTheDocument(); + fireEvent.change(screen.getByPlaceholderText('AWS Account ID'), { + target: { value: '123123123' }, + }); + + await userEvent.click( + screen.getByRole('button', { name: /create join token/i }) + ); + + expect( + screen.queryByText(/create a new join token/i) + ).not.toBeInTheDocument(); + expect(screen.getByText('the_token')).toBeInTheDocument(); + }); + + test('a rule cannot be deleted if it is the only rule', async () => { + render(); + await userEvent.click( + screen.getByRole('button', { name: /create new token/i }) + ); + + const buttons = screen.queryAllByTestId('delete_rule'); + expect(buttons).toHaveLength(0); + }); + + test('a rule can be deleted more than one rule exists', async () => { + render(); + await userEvent.click( + screen.getByRole('button', { name: /create new token/i }) + ); + + fireEvent.click(screen.getByText('Add another AWS Rule')); + + const buttons = screen.queryAllByTestId('delete_rule'); + expect(buttons).toHaveLength(2); + }); +}); + +const Component = () => { + const ctx = createTeleportContext(); + jest + .spyOn(ctx.joinTokenService, 'fetchJoinTokens') + .mockResolvedValue({ items: tokens.map(makeJoinToken) }); + + jest.spyOn(ctx.joinTokenService, 'createJoinToken').mockResolvedValue( + makeJoinToken({ + id: 'the_token', + safeName: 'the_token', + bot_name: '', + expiry: '3024-07-26T11:52:48.320045Z', + roles: ['Node'], + isStatic: false, + method: 'iam', + allow: [ + { + aws_account: '1234444', + aws_arn: 'asdf', + }, + ], + content: 'fake content', + }) + ); + + return ( + + + + ); +}; + +const tokens = [ + { + id: '123123ffff', + safeName: '123123ffff', + bot_name: '', + expiry: '3024-07-26T11:52:48.320045Z', + roles: ['Node'], + isStatic: false, + method: 'iam', + allow: [ + { + aws_account: '1234444', + aws_arn: 'asdf', + }, + ], + content: 'fake content', + }, + { + id: 'rrrrr', + safeName: 'rrrrr', + bot_name: '7777777', + expiry: '3024-07-26T12:05:48.08241Z', + roles: ['Bot', 'Node'], + isStatic: false, + method: 'iam', + allow: [ + { + aws_account: '445555444', + }, + ], + content: 'fake content', + }, +]; diff --git a/web/packages/teleport/src/JoinTokens/JoinTokens.tsx b/web/packages/teleport/src/JoinTokens/JoinTokens.tsx index b4cd1fd3049df..1364471a2c68c 100644 --- a/web/packages/teleport/src/JoinTokens/JoinTokens.tsx +++ b/web/packages/teleport/src/JoinTokens/JoinTokens.tsx @@ -30,6 +30,7 @@ import { MenuItem, ButtonWarning, ButtonSecondary, + Button, } from 'design'; import Table, { Cell } from 'design/DataTable'; import { Warning } from 'design/Icon'; @@ -56,6 +57,8 @@ import { JoinToken } from 'teleport/services/joinToken'; import { Resource, KindJoinToken } from 'teleport/services/resources'; import ResourceEditor from 'teleport/components/ResourceEditor'; +import { UpsertJoinTokenDialog } from './UpsertJoinTokenDialog'; + function makeTokenResource(token: JoinToken): Resource { return { id: token.id, @@ -67,6 +70,8 @@ function makeTokenResource(token: JoinToken): Resource { export const JoinTokens = () => { const ctx = useTeleport(); + const [creatingToken, setCreatingToken] = useState(false); + const [editingToken, setEditingToken] = useState(null); const [tokenToDelete, setTokenToDelete] = useState(null); const [joinTokensAttempt, runJoinTokensAttempt, setJoinTokensAttempt] = useAsync(async () => await ctx.joinTokenService.fetchJoinTokens()); @@ -76,25 +81,17 @@ export const JoinTokens = () => { { join_token: '' } // we are only editing for now, so template can be empty ); - async function handleSave(content: string): Promise { - const token = await ctx.joinTokenService.upsertJoinToken({ content }); + function updateTokenList(token: JoinToken): JoinToken[] { let items = [...joinTokensAttempt.data.items]; - if (resources.status === 'creating') { + if (creatingToken) { items.push(token); } else { - let tokenExistsInPreviousList = false; const newItems = items.map(item => { if (item.id === token.id) { - tokenExistsInPreviousList = true; return token; } return item; }); - // in the edge case that someone only edits the name of the token, it will return - // a "new" token via the upsert, and therefore should be treated as a new token - if (!tokenExistsInPreviousList) { - newItems.push(token); - } items = newItems; } setJoinTokensAttempt({ @@ -102,10 +99,19 @@ export const JoinTokens = () => { status: 'success', statusText: '', }); + return items; } - const [deleteTokenAttempt, runDeleteTokenAttempt] = useAsync( - async (token: string) => { + async function handleSave(content: string): Promise { + const token = await ctx.joinTokenService.upsertJoinTokenYAML( + { content }, + resources.item.id + ); + updateTokenList(token); + } + + const [deleteTokenAttempt, runDeleteTokenAttempt, setDeleteTokenAttempt] = + useAsync(async (token: string) => { await ctx.joinTokenService.deleteJoinToken(token); setJoinTokensAttempt({ status: 'success', @@ -115,8 +121,9 @@ export const JoinTokens = () => { }, }); setTokenToDelete(null); - } - ); + setEditingToken(null); + setCreatingToken(false); + }); useEffect(() => { runJoinTokensAttempt(); @@ -132,94 +139,142 @@ export const JoinTokens = () => { alignItems="center" > Join Tokens - - - {joinTokensAttempt.status === 'error' && ( - {joinTokensAttempt.statusText} + {!creatingToken && !editingToken && ( + )} - {deleteTokenAttempt.status === 'error' && ( - {deleteTokenAttempt.statusText} - )} - {joinTokensAttempt.status === 'success' && ( - , - }, - { - key: 'method', - headerText: 'Join Method', - isSortable: true, - }, - { - key: 'roles', - headerText: 'Roles', - isSortable: false, - render: renderRolesCell, - }, - // expiryText is non render and used for searching - { - key: 'expiryText', - isNonRender: true, - }, - // expiry is used for sorting, but we display the expiryText value - { - key: 'expiry', - headerText: 'Expires in', - isSortable: true, - render: ({ expiry, expiryText, isStatic, method }) => { - const now = new Date(); - const isLongLived = - isAfter(expiry, addHours(now, 24)) && method === 'token'; - return ( - - - {expiryText} - {(isLongLived || isStatic) && ( - - - - )} - - - ); + + + + {joinTokensAttempt.status === 'error' && ( + {joinTokensAttempt.statusText} + )} + {joinTokensAttempt.status === 'success' && ( +
, + }, + { + key: 'method', + headerText: 'Join Method', + isSortable: true, + }, + { + key: 'roles', + headerText: 'Roles', + isSortable: false, + render: renderRolesCell, + }, + // expiryText is non render and used for searching + { + key: 'expiryText', + isNonRender: true, }, - }, - { - altKey: 'options-btn', - render: (token: JoinToken) => ( - resources.edit(token.id)} - onDelete={() => setTokenToDelete(token)} - /> - ), - }, - ]} - emptyText="No active join tokens found" - pagination={{ pageSize: 30, pagerPosition: 'top' }} - customSearchMatchers={[searchMatcher]} - initialSort={{ - key: 'expiry', - dir: 'ASC', + // expiry is used for sorting, but we display the expiryText value + { + key: 'expiry', + headerText: 'Expires in', + isSortable: true, + render: ({ expiry, expiryText, isStatic, method }) => { + const now = new Date(); + const isLongLived = + isAfter(expiry, addHours(now, 24)) && method === 'token'; + return ( + + + {expiryText} + {(isLongLived || isStatic) && ( + + + + )} + + + ); + }, + }, + { + altKey: 'options-btn', + render: (token: JoinToken) => ( + { + // prefer editing in the standard form + // if we support that join method + if ( + token.method === 'iam' || + token.method === 'gcp' || + token.method === 'token' + ) { + setEditingToken(token); + return; + } + // otherwise, edit in yaml editor + setEditingToken(null); // close any editing token + resources.edit(token.id); + }} + onDelete={() => setTokenToDelete(token)} + /> + ), + }, + ]} + emptyText="No active join tokens found" + pagination={{ pageSize: 30, pagerPosition: 'top' }} + customSearchMatchers={[searchMatcher]} + initialSort={{ + key: 'expiry', + dir: 'ASC', + }} + /> + )} + {joinTokensAttempt.status === 'processing' && ( + + + + )} + + + {(creatingToken || !!editingToken) && ( + { + setCreatingToken(false); + setEditingToken(null); }} /> )} - {joinTokensAttempt.status === 'processing' && ( - - - - )} - + {tokenToDelete && ( setTokenToDelete(null)} + onClose={() => { + setDeleteTokenAttempt({ + status: 'success', + statusText: '', + data: null, + }); + setTokenToDelete(null); + }} onDelete={() => runDeleteTokenAttempt(tokenToDelete.id)} attempt={deleteTokenAttempt} /> diff --git a/web/packages/teleport/src/JoinTokens/UpsertJoinTokenDialog.tsx b/web/packages/teleport/src/JoinTokens/UpsertJoinTokenDialog.tsx new file mode 100644 index 0000000000000..b3ba66f4ced67 --- /dev/null +++ b/web/packages/teleport/src/JoinTokens/UpsertJoinTokenDialog.tsx @@ -0,0 +1,367 @@ +/** + * 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 . + */ + +import { useState } from 'react'; + +import { + Flex, + Text, + Box, + ButtonIcon, + ButtonText, + ButtonPrimary, + ButtonSecondary, + Alert, +} from 'design'; +import styled from 'styled-components'; +import { HoverTooltip } from 'shared/components/ToolTip'; +import { Cross } from 'design/Icon'; +import Validation from 'shared/components/Validation'; +import FieldInput from 'shared/components/FieldInput'; +import { requiredField } from 'shared/components/Validation/rules'; +import { FieldSelect } from 'shared/components/FieldSelect'; +import { Option } from 'shared/components/Select'; +import { useAsync } from 'shared/hooks/useAsync'; + +import { useTeleport } from 'teleport'; +import { + AWSRules, + CreateJoinTokenRequest, + JoinMethod, + JoinRole, + JoinToken, +} from 'teleport/services/joinToken'; + +import { JoinTokenGCPForm, JoinTokenIAMForm } from './JoinTokenForms'; + +const maxWidth = '550px'; + +const joinRoleOptions: OptionJoinRole[] = [ + 'App', + 'Node', + 'Db', + 'Kube', + 'Bot', + 'WindowsDesktop', + 'Discovery', +].map(role => ({ value: role as JoinRole, label: role as JoinRole })); + +const availableJoinMethods: OptionJoinMethod[] = ['iam', 'gcp'].map(method => ({ + value: method as JoinMethod, + label: method as JoinMethod, +})); + +export type OptionGCP = Option; +type OptionJoinMethod = Option; +type OptionJoinRole = Option; +type NewJoinTokenGCPState = { + project_ids: OptionGCP[]; + service_accounts: OptionGCP[]; + locations: OptionGCP[]; +}; + +export type NewJoinTokenState = { + name: string; + // bot_name is only required when Bot is selected in the roles + bot_name?: string; + method: OptionJoinMethod; + roles: OptionJoinRole[]; + iam: AWSRules[]; + gcp: NewJoinTokenGCPState[]; +}; + +export const defaultNewTokenState: NewJoinTokenState = { + name: '', + bot_name: '', + method: { value: 'iam', label: 'iam' }, + roles: [], + iam: [{ aws_account: '', aws_arn: '' }], + gcp: [{ project_ids: [], service_accounts: [], locations: [] }], +}; + +function makeDefaultEditState(token: JoinToken): NewJoinTokenState { + return { + name: token.id, + bot_name: token.bot_name, + method: { + value: token.method, + label: token.method, + } as OptionJoinMethod, + roles: token.roles.map(r => ({ value: r, label: r })) as OptionJoinRole[], + iam: token.allow, + gcp: token.gcp?.allow.map(r => ({ + project_ids: r.project_ids?.map(i => ({ value: i, label: i })), + service_accounts: r.service_accounts?.map(i => ({ value: i, label: i })), + locations: r.locations?.map(i => ({ value: i, label: i })), + })), + }; +} + +export const UpsertJoinTokenDialog = ({ + onClose, + updateTokenList, + editToken, + editTokenWithYAML, +}: { + onClose(): void; + updateTokenList: (token: JoinToken) => void; + editToken?: JoinToken; + editTokenWithYAML: (tokenId: string) => void; +}) => { + const ctx = useTeleport(); + const [newTokenState, setNewTokenState] = useState( + editToken ? makeDefaultEditState(editToken) : defaultNewTokenState + ); + + const [createTokenAttempt, runCreateTokenAttempt] = useAsync( + async (req: CreateJoinTokenRequest) => { + const token = await ctx.joinTokenService.createJoinToken(req); + updateTokenList(token); + onClose(); + } + ); + + function reset(validator) { + validator.reset(); + setNewTokenState(defaultNewTokenState); + } + + async function save(validator) { + if (!validator.validate()) { + return; + } + + const request: CreateJoinTokenRequest = { + name: newTokenState.name, + roles: newTokenState.roles.map(r => r.value), + join_method: newTokenState.method.value, + }; + + if (newTokenState.method.value === 'iam') { + request.allow = newTokenState.iam; + } + + if (request.roles.includes('Bot')) { + request.bot_name = newTokenState.bot_name; + } + + if (newTokenState.method.value === 'gcp') { + const gcp = { + allow: newTokenState.gcp.map(rule => ({ + project_ids: rule.project_ids?.map(id => id.value), + locations: rule.locations?.map(loc => loc.value), + service_accounts: rule.service_accounts?.map( + account => account.value + ), + })), + }; + request.gcp = gcp; + } + + runCreateTokenAttempt(request); + } + + function setTokenRoles(roles: OptionJoinRole[]) { + setNewTokenState(prevState => ({ + ...prevState, + roles: roles || [], + })); + } + + function setTokenMethod(method: OptionJoinMethod) { + // set the method and reset the token rules per type for a fresh form + setNewTokenState(prevState => ({ + ...prevState, + method, + iam: [{ aws_account: '', aws_arn: '' }], // default + })); + } + + function setTokenField(fieldName: string, value: string) { + setNewTokenState(prevState => ({ + ...prevState, + [fieldName]: value, + })); + } + + return ( + + + + + + + + + + + {editToken ? `Edit Token` : 'Create a New Join Token'} + + + {editToken && ( + { + onClose(); + editTokenWithYAML(editToken.id); + }} + > + Use YAML editor + + )} + + + {({ validator }) => ( + + {createTokenAttempt.status === 'error' && ( + {createTokenAttempt.statusText} + )} + {!editToken && ( // We only want to change the method when creating a new token + + )} + {newTokenState.method.value !== 'token' && ( // if the method is token, we generate the name for them on the backend + setTokenField('name', e.target.value)} + readonly={!!editToken} + /> + )} + + {newTokenState.roles.some(i => i.value === 'Bot') && ( // if Bot is included, we must get a bot name as well + setTokenField('bot_name', e.target.value)} + /> + )} + {newTokenState.method.value === 'iam' && ( + setNewTokenState(newState)} + /> + )} + {newTokenState.method.value === 'gcp' && ( + setNewTokenState(newState)} + /> + )} + theme.colors.levels.sunken}; + border-top: 1px solid + ${props => props.theme.colors.spotBackground[1]}; + `} + > + save(validator)} + disabled={createTokenAttempt.status === 'processing'} + > + {editToken ? 'Edit' : 'Create'} Join Token + + { + reset(validator); + onClose(); + }} + disabled={false} + > + Cancel + + + + )} + + + + ); +}; + +export const RuleBox = styled(Box)` + border-color: ${props => + props.theme.colors.interactive.tonal.neutral[0].background}; + border-width: 2px; + border-style: solid; + border-radius: ${props => props.theme.radii[2]}px; + + margin-bottom: ${props => props.theme.space[3]}px; + + padding: ${props => props.theme.space[3]}px; +`; diff --git a/web/packages/teleport/src/Main/Main.tsx b/web/packages/teleport/src/Main/Main.tsx index c8436aa1cce96..7545080a0757f 100644 --- a/web/packages/teleport/src/Main/Main.tsx +++ b/web/packages/teleport/src/Main/Main.tsx @@ -327,7 +327,7 @@ export const useNoMinWidth = () => { }, []); }; -const ContentMinWidth = ({ children }: { children: ReactNode }) => { +export const ContentMinWidth = ({ children }: { children: ReactNode }) => { const [enforceMinWidth, setEnforceMinWidth] = useState(true); return ( diff --git a/web/packages/teleport/src/SamlApplications/useSamlAppActions.tsx b/web/packages/teleport/src/SamlApplications/useSamlAppActions.tsx new file mode 100644 index 0000000000000..929f4ff149e78 --- /dev/null +++ b/web/packages/teleport/src/SamlApplications/useSamlAppActions.tsx @@ -0,0 +1,118 @@ +/** + * 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 . + */ + +import React, { createContext, useContext } from 'react'; +import { Attempt } from 'shared/hooks/useAsync'; + +import { SamlMeta } from 'teleport/Discover/useDiscover'; + +import type { SamlAppToDelete } from 'teleport/services/samlidp/types'; +import type { ResourceSpec } from 'teleport/Discover/SelectResource/types'; +import type { Access } from 'teleport/services/user'; + +/** + * SamlAppAction defines Saml application edit and delete actions. + */ +export interface SamlAppAction { + /** + * actions controls Saml menu button view and edit and delete onClick behaviour. + */ + actions: { + /** + * showActions dictates whether to show or hide the Saml menu button. + */ + showActions: boolean; + /** + * startEdit triggers Saml app edit flow. + */ + startEdit: (resourceSpec: ResourceSpec) => void; + /** + * startDelete triggers Saml app delete flow. + */ + startDelete: (resourceSpec: ResourceSpec) => void; + }; + /** + * currentAction specifies edit or delete mode. + */ + currentAction?: SamlAppActionMode; + /** + * deleteSamlAppAttempt is an attempt to delete Saml + * app in the backend. + */ + deleteSamlAppAttempt?: Attempt; + /** + * samlAppToDelete defines Saml app item that is to be + * deleted from the unified view. + */ + samlAppToDelete?: SamlAppToDelete; + /** + * fetchSamlResourceAttempt is an attempt to fetch + * Saml resource spec from the backend. It is used to + * pre-populate input fields in the Saml Discover flow. + */ + fetchSamlResourceAttempt?: Attempt; + /** + * resourceSpec holds current Saml app resource spec. + */ + resourceSpec?: ResourceSpec; + /** + * userSamlIdPPerm holds user's RBAC permissions to + * saml_idp_service_provider resource. + */ + userSamlIdPPerm?: Access; + /** + * clearAction clears edit or delete flow. + */ + clearAction?: () => void; + /** + * onDelete handles Saml app delete in the backend. + */ + onDelete?: () => void; +} + +export const SamlAppActionContext = createContext(null); + +export function useSamlAppAction() { + return useContext(SamlAppActionContext); +} + +/** + * SamlAppActionProvider is a dummy provider to satisfy + * SamlAppActionContext in Teleport community edition. + */ +export function SamlAppActionProvider({ + children, +}: { + children: React.ReactNode; +}) { + const value: SamlAppAction = { + actions: { + showActions: false, + startEdit: null, + startDelete: null, + }, + }; + + return ( + + {children} + + ); +} + +export type SamlAppActionMode = 'edit' | 'delete'; diff --git a/web/packages/teleport/src/UnifiedResources/ResourceActionButton.tsx b/web/packages/teleport/src/UnifiedResources/ResourceActionButton.tsx index d8c49c168d16b..05fcfe3de05ce 100644 --- a/web/packages/teleport/src/UnifiedResources/ResourceActionButton.tsx +++ b/web/packages/teleport/src/UnifiedResources/ResourceActionButton.tsx @@ -16,14 +16,13 @@ * along with this program. If not, see . */ -import React, { useState, Dispatch, SetStateAction } from 'react'; +import React, { useState } from 'react'; import { ButtonBorder, ButtonWithMenu, MenuItem } from 'design'; import { LoginItem, MenuLogin } from 'shared/components/MenuLogin'; import { AwsLaunchButton } from 'shared/components/AwsLaunchButton'; import { UnifiedResource } from 'teleport/services/agents'; import cfg from 'teleport/config'; - import useTeleport from 'teleport/useTeleport'; import { Database } from 'teleport/services/databases'; import { openNewTab } from 'teleport/lib/util'; @@ -34,24 +33,22 @@ import KubeConnectDialog from 'teleport/Kubes/ConnectDialog'; import useStickyClusterId from 'teleport/useStickyClusterId'; import { Node, sortNodeLogins } from 'teleport/services/nodes'; import { App } from 'teleport/services/apps'; - import { ResourceKind } from 'teleport/Discover/Shared'; - import { DiscoverEventResource } from 'teleport/services/userEvent'; +import { useSamlAppAction } from 'teleport/SamlApplications/useSamlAppActions'; import type { ResourceSpec } from 'teleport/Discover/SelectResource/types'; type Props = { resource: UnifiedResource; - setResourceSpec?: Dispatch>; }; -export const ResourceActionButton = ({ resource, setResourceSpec }: Props) => { +export const ResourceActionButton = ({ resource }: Props) => { switch (resource.kind) { case 'node': return ; case 'app': - return ; + return ; case 'db': return ; case 'kube_cluster': @@ -145,9 +142,8 @@ const DesktopConnect = ({ desktop }: { desktop: Desktop }) => { type AppLaunchProps = { app: App; - setResourceSpec?: Dispatch>; }; -const AppLaunch = ({ app, setResourceSpec }: AppLaunchProps) => { +const AppLaunch = ({ app }: AppLaunchProps) => { const { name, launchUrl, @@ -161,6 +157,7 @@ const AppLaunch = ({ app, setResourceSpec }: AppLaunchProps) => { samlAppSsoUrl, samlAppPreset, } = app; + const { actions, userSamlIdPPerm } = useSamlAppAction(); if (awsConsole) { return ( { ); } - function handleSamlAppEditButtonClick() { - setResourceSpec({ - name: name, - event: DiscoverEventResource.SamlApplication, - kind: ResourceKind.SamlApplication, - samlMeta: { preset: samlAppPreset }, - icon: 'application', - keywords: 'saml', - }); - } if (samlApp) { - if (setResourceSpec) { + if (actions.showActions) { + const currentSamlAppSpec: ResourceSpec = { + name: name, + event: DiscoverEventResource.SamlApplication, + kind: ResourceKind.SamlApplication, + samlMeta: { preset: samlAppPreset }, + icon: 'application', + keywords: 'saml', + }; return ( { forwardedAs="a" title="Log in to SAML application" > - Edit + actions.startEdit(currentSamlAppSpec)} + disabled={!userSamlIdPPerm.edit} // disable props does not disable onClick + > + Edit + + actions.startDelete(currentSamlAppSpec)} + disabled={!userSamlIdPPerm.remove} // disable props does not disable onClick + > + Delete + ); } else { diff --git a/web/packages/teleport/src/UnifiedResources/UnifiedResources.tsx b/web/packages/teleport/src/UnifiedResources/UnifiedResources.tsx index cc292eadfc11e..d3353edc34b7c 100644 --- a/web/packages/teleport/src/UnifiedResources/UnifiedResources.tsx +++ b/web/packages/teleport/src/UnifiedResources/UnifiedResources.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useState, useMemo } from 'react'; import { Flex } from 'design'; import { Danger } from 'design/Alert'; @@ -50,6 +50,10 @@ import { encodeUrlQueryParams } from 'teleport/components/hooks/useUrlFiltering' import Empty, { EmptyStateInfo } from 'teleport/components/Empty'; import { FeatureFlags } from 'teleport/types'; import { UnifiedResource } from 'teleport/services/agents'; +import { + useSamlAppAction, + SamlAppActionProvider, +} from 'teleport/SamlApplications/useSamlAppActions'; import { ResourceActionButton } from './ResourceActionButton'; import SearchPanel from './SearchPanel'; @@ -59,11 +63,13 @@ export function UnifiedResources() { return ( - + + + ); } @@ -149,7 +155,12 @@ export function ClusterResources({ getClusterPinnedResources: getCurrentClusterPinnedResources, }; - const { fetch, resources, attempt, clear } = useUnifiedResourcesFetch({ + const { + fetch, + resources: unfilteredResources, + attempt, + clear, + } = useUnifiedResourcesFetch({ fetchFunc: useCallback( async (paginationParams, signal) => { const response = await teleCtx.resourceService.fetchUnifiedResources( @@ -186,6 +197,21 @@ export function ClusterResources({ ] ), }); + const { samlAppToDelete } = useSamlAppAction(); + const resources = useMemo( + () => + samlAppToDelete?.backendDeleted + ? unfilteredResources.filter( + res => + !( + res.kind === 'app' && + res.samlApp && + res.name === samlAppToDelete.name + ) + ) + : unfilteredResources, + [samlAppToDelete, unfilteredResources] + ); // This state is used to recognize when the `params` value has changed, // and reset the overall state of `useUnifiedResourcesFetch` hook. It's tempting to use a diff --git a/web/packages/teleport/src/config.ts b/web/packages/teleport/src/config.ts index 226e7faa619bd..d54da559ce425 100644 --- a/web/packages/teleport/src/config.ts +++ b/web/packages/teleport/src/config.ts @@ -263,7 +263,7 @@ const cfg = { connectMyComputerLoginsPath: '/v1/webapi/connectmycomputer/logins', joinTokenPath: '/v1/webapi/token', - joinTokenYamlPath: '/v1/webapi/token/yaml', + joinTokenYamlPath: '/v1/webapi/tokens/yaml', joinTokensPath: '/v1/webapi/tokens', dbScriptPath: '/scripts/:token/install-database.sh', nodeScriptPath: '/scripts/:token/install-node.sh', diff --git a/web/packages/teleport/src/features.tsx b/web/packages/teleport/src/features.tsx index c687344e915e6..15bc9cc24f935 100644 --- a/web/packages/teleport/src/features.tsx +++ b/web/packages/teleport/src/features.tsx @@ -25,6 +25,7 @@ import { ClipboardUser, Cluster, Integrations as IntegrationsIcon, + Key, Laptop, ListAddCheck, ListThin, @@ -125,6 +126,16 @@ export class FeatureNodes implements TeleportFeature { // TODO (avatus) add navigationItem when ready to release export class FeatureJoinTokens implements TeleportFeature { category = NavigationCategory.Management; + section = ManagementSection.Access; + navigationItem = { + title: NavTitle.JoinTokens, + icon: Key, + exact: true, + getLink() { + return cfg.getJoinTokensRoute(); + }, + }; + route = { title: NavTitle.JoinTokens, path: cfg.routes.joinTokens, diff --git a/web/packages/teleport/src/lib/term/terminal.ts b/web/packages/teleport/src/lib/term/terminal.ts index 7816a78f92f2c..cc8f68a2ff12b 100644 --- a/web/packages/teleport/src/lib/term/terminal.ts +++ b/web/packages/teleport/src/lib/term/terminal.ts @@ -16,13 +16,13 @@ * along with this program. If not, see . */ -import 'xterm/css/xterm.css'; -import { ITheme, Terminal } from 'xterm'; -import { FitAddon } from 'xterm-addon-fit'; -import { WebglAddon } from 'xterm-addon-webgl'; +import '@xterm/xterm/css/xterm.css'; +import { ITheme, Terminal } from '@xterm/xterm'; +import { FitAddon } from '@xterm/addon-fit'; +import { WebglAddon } from '@xterm/addon-webgl'; +import { WebLinksAddon } from '@xterm/addon-web-links'; +import { CanvasAddon } from '@xterm/addon-canvas'; import { debounce, isInteger } from 'shared/utils/highbar'; -import { WebLinksAddon } from 'xterm-addon-web-links'; -import { CanvasAddon } from 'xterm-addon-canvas'; import Logger from 'shared/libs/logger'; import cfg from 'teleport/config'; diff --git a/web/packages/teleport/src/services/api/api.test.ts b/web/packages/teleport/src/services/api/api.test.ts index 3f840f4345a3a..a662a5fcc4d90 100644 --- a/web/packages/teleport/src/services/api/api.test.ts +++ b/web/packages/teleport/src/services/api/api.test.ts @@ -120,6 +120,31 @@ describe('api.fetch', () => { }, }); }); + + const customContentType = { + ...customOpts, + headers: { + Accept: 'application/json', + 'Content-Type': 'multipart/form-data', + }, + }; + + test('with customOptions including custom content-type', async () => { + await api.fetch('/something', customContentType, null); + expect(mockedFetch).toHaveBeenCalledTimes(1); + + const firstCall = mockedFetch.mock.calls[0]; + const [, actualRequestOptions] = firstCall; + + expect(actualRequestOptions).toStrictEqual({ + ...defaultRequestOptions, + ...customOpts, + headers: { + ...customContentType.headers, + ...getAuthHeaders(), + }, + }); + }); }); // The code below should guard us from changes to api.fetchJson which would cause it to lose type diff --git a/web/packages/teleport/src/services/api/api.ts b/web/packages/teleport/src/services/api/api.ts index 027fa3b30c04f..8490cecac2125 100644 --- a/web/packages/teleport/src/services/api/api.ts +++ b/web/packages/teleport/src/services/api/api.ts @@ -77,12 +77,21 @@ const api = { ); }, - deleteWithHeaders(url, headers?: Record, signal?) { - return api.fetch(url, { - method: 'DELETE', - headers, - signal, - }); + deleteWithHeaders( + url, + headers?: Record, + signal?, + webauthnResponse?: WebauthnAssertionResponse + ) { + return api.fetchJsonWithMfaAuthnRetry( + url, + { + method: 'DELETE', + headers, + signal, + }, + webauthnResponse + ); }, // TODO (avatus) add abort signal to this @@ -97,6 +106,23 @@ const api = { ); }, + putWithHeaders( + url, + data, + headers?: Record, + webauthnResponse?: WebauthnAssertionResponse + ) { + return api.fetchJsonWithMfaAuthnRetry( + url, + { + body: JSON.stringify(data), + method: 'PUT', + headers, + }, + webauthnResponse + ); + }, + /** * fetchJsonWithMfaAuthnRetry calls on `api.fetch` and * processes the response. diff --git a/web/packages/teleport/src/services/joinToken/joinToken.ts b/web/packages/teleport/src/services/joinToken/joinToken.ts index 24deb61270092..33b3faeefc809 100644 --- a/web/packages/teleport/src/services/joinToken/joinToken.ts +++ b/web/packages/teleport/src/services/joinToken/joinToken.ts @@ -24,6 +24,8 @@ import { makeLabelMapOfStrArrs } from '../agents/make'; import makeJoinToken from './makeJoinToken'; import { JoinToken, JoinRule, JoinTokenRequest } from './types'; +const TeleportTokenNameHeader = 'X-Teleport-TokenName'; + class JoinTokenService { // TODO (avatus) refactor this code to eventually use `createJoinToken` fetchJoinToken( @@ -46,16 +48,28 @@ class JoinTokenService { .then(makeJoinToken); } - // TODO (avatus) for the first iteration, we will create tokens using only yaml and - // slowly create a form for each token type. - upsertJoinToken(req: JoinTokenRequest): Promise { + upsertJoinTokenYAML( + req: JoinTokenRequest, + tokenName: string + ): Promise { return api - .put(cfg.getJoinTokenYamlUrl(), { - content: req.content, - }) + .putWithHeaders( + cfg.getJoinTokenYamlUrl(), + { + content: req.content, + }, + { + [TeleportTokenNameHeader]: tokenName, + 'Content-Type': 'application/json', + } + ) .then(makeJoinToken); } + createJoinToken(req: JoinTokenRequest): Promise { + return api.post(cfg.getJoinTokensUrl(), req).then(makeJoinToken); + } + fetchJoinTokens(signal: AbortSignal = null): Promise<{ items: JoinToken[] }> { return api.get(cfg.getJoinTokensUrl(), signal).then(resp => { return { @@ -67,7 +81,7 @@ class JoinTokenService { deleteJoinToken(id: string, signal: AbortSignal = null) { return api.deleteWithHeaders( cfg.getJoinTokensUrl(), - { 'X-Teleport-TokenName': id }, + { [TeleportTokenNameHeader]: id }, signal ); } diff --git a/web/packages/teleport/src/services/joinToken/makeJoinToken.ts b/web/packages/teleport/src/services/joinToken/makeJoinToken.ts index 49772fd38d802..57a134dfe3588 100644 --- a/web/packages/teleport/src/services/joinToken/makeJoinToken.ts +++ b/web/packages/teleport/src/services/joinToken/makeJoinToken.ts @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { formatDistanceStrict } from 'date-fns'; +import { formatDistanceStrict, differenceInYears } from 'date-fns'; import type { JoinToken } from './types'; @@ -28,6 +28,9 @@ export default function makeToken(json): JoinToken { id, roles, isStatic, + allow, + gcp, + bot_name, expiry, method, suggestedLabels, @@ -41,7 +44,10 @@ export default function makeToken(json): JoinToken { id, isStatic, safeName, + bot_name, method, + allow, + gcp, roles: roles?.sort((a, b) => a.localeCompare(b)) || [], suggestedLabels: labels, internalResourceId: extractInternalResourceId(labels), @@ -52,14 +58,24 @@ export default function makeToken(json): JoinToken { } function getExpiryText(expiry: string, isStatic: boolean): string { + const expiryDate = new Date(expiry); + const now = new Date(); + + // dynamically configured tokens that "never expire" are set to actually expire + // 1000 years from now. We can just check if the expiry date is over 100 years away + // and show a "never" text instead of 999years. If a customer is still running teleport + // and using this token for over 100 years and they see the 899, maybe they + // actually care about the date. + const yearsDifference = differenceInYears(expiryDate, now); // a manually configured token with no TTL will be set to zero date - if (expiry == '0001-01-01T00:00:00Z' || isStatic) { + if (expiry == '0001-01-01T00:00:00Z' || isStatic || yearsDifference > 100) { return 'never'; } if (!expiry) { return ''; } - return formatDistanceStrict(new Date(), new Date(expiry)); + + return formatDistanceStrict(now, expiryDate); } function extractInternalResourceId(labels: any[]) { diff --git a/web/packages/teleport/src/services/joinToken/types.ts b/web/packages/teleport/src/services/joinToken/types.ts index 8179bb29e35e9..a36c1a975d1bd 100644 --- a/web/packages/teleport/src/services/joinToken/types.ts +++ b/web/packages/teleport/src/services/joinToken/types.ts @@ -24,6 +24,8 @@ export type JoinToken = { // the first 16 chars will be * and the rest of the token's chars will be visible // ex. ****************asdf1234 safeName: string; + // bot_name is present on tokens with Bot in their join roles + bot_name?: string; isStatic: boolean; // the join method of the token method: string; @@ -41,6 +43,10 @@ export type JoinToken = { internalResourceId?: string; // yaml content of the resource content: string; + allow?: AWSRules[]; + gcp?: { + allow: GCPRules[]; + }; }; // JoinRole defines built-in system roles and are roles associated with @@ -50,6 +56,7 @@ export type JoinToken = { // - 'Db' is a role for a database proxy in the cluster // - 'Kube' is a role for a kube service // - 'Node' is a role for a node in the cluster +// - 'Bot' for MachineID (when set, "spec.bot_name" must be set in the token) // - 'WindowsDesktop' is a role for a windows desktop service. // - 'Discovery' is a role for a discovery service. export type JoinRole = @@ -57,6 +64,7 @@ export type JoinRole = | 'Node' | 'Db' | 'Kube' + | 'Bot' | 'WindowsDesktop' | 'Discovery'; @@ -82,6 +90,37 @@ export type JoinRule = { awsAccountId: string; // awsArn is used for the IAM join method. awsArn?: string; + regions?: string[]; +}; + +export type AWSRules = { + aws_account: string; // naming kept consistent with backend spec + aws_arn?: string; +}; + +export type GCPRules = { + project_ids: string[]; + locations: string[]; + service_accounts: string[]; +}; + +export type JoinTokenRulesObject = AWSRules | GCPRules; + +export type CreateJoinTokenRequest = { + name: string; + // roles is a list of join roles, since there can be more than + // one role associated with a token. + roles: JoinRole[]; + // bot_name only needs to be specified if "Bot" is in the selected roles. + // otherwise, it is ignored + bot_name?: string; + join_method: JoinMethod; + // rules is a list of allow rules associated with the join token + // and the node using this token must match one of the rules. + allow?: JoinTokenRulesObject[]; + gcp?: { + allow: GCPRules[]; + }; }; export type JoinTokenRequest = { diff --git a/web/packages/teleport/src/services/samlidp/types.ts b/web/packages/teleport/src/services/samlidp/types.ts index 854145f282f4a..eb392c55479e8 100644 --- a/web/packages/teleport/src/services/samlidp/types.ts +++ b/web/packages/teleport/src/services/samlidp/types.ts @@ -76,3 +76,21 @@ export type SamlGcpWorkforce = { poolName: string; poolProviderName: string; }; + +/** + * SamlAppToDelete is used to define the name of an + * SAML app item to be deleted and its deletion state in the + * backend. Intended to be used in the unified resource view. + */ +export type SamlAppToDelete = { + /** + * name is the name of Saml app item to delete. + */ + name: string; + // kind: string; + /** + * backendDeleted specifies if the item is deleted + * in the backend. + */ + backendDeleted: boolean; +}; diff --git a/web/packages/teleterm/package.json b/web/packages/teleterm/package.json index 95f9d70a9fa0f..9f6090df01f37 100644 --- a/web/packages/teleterm/package.json +++ b/web/packages/teleterm/package.json @@ -42,6 +42,8 @@ "@types/node-forge": "^1.3.11", "@types/tar-fs": "^2.0.4", "@types/whatwg-url": "^11.0.5", + "@xterm/xterm": "^5.5.0", + "@xterm/addon-fit": "^0.10.0", "electron": "31.1.0", "electron-builder": "^25.0.1", "electron-notarize": "^1.2.2", @@ -52,8 +54,6 @@ "react-dnd": "^14.0.4", "react-dnd-html5-backend": "^14.0.2", "whatwg-url": "^14.0.0", - "xterm": "^5.3.0", - "xterm-addon-fit": "^0.8.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.23.1" }, diff --git a/web/packages/teleterm/src/mainProcess/mainProcess.ts b/web/packages/teleterm/src/mainProcess/mainProcess.ts index 4430dc7c6ba8e..813658ae24111 100644 --- a/web/packages/teleterm/src/mainProcess/mainProcess.ts +++ b/web/packages/teleterm/src/mainProcess/mainProcess.ts @@ -44,6 +44,7 @@ import { ChildProcessAddresses, MainProcessIpc, RendererIpc, + TERMINATE_MESSAGE, } from 'teleterm/mainProcess/types'; import { getAssetPath } from 'teleterm/mainProcess/runtimeSettings'; import { RootClusterUri } from 'teleterm/ui/uri'; @@ -143,7 +144,11 @@ export default class MainProcess { terminateWithTimeout(this.tshdProcess, 10_000, () => { this.gracefullyKillTshdProcess(); }), - terminateWithTimeout(this.sharedProcess), + terminateWithTimeout(this.sharedProcess, 5_000, process => + // process.kill doesn't allow running a cleanup code in the child process + // on Windows + process.send(TERMINATE_MESSAGE) + ), this.agentRunner.killAll(), ]); } diff --git a/web/packages/teleterm/src/mainProcess/types.ts b/web/packages/teleterm/src/mainProcess/types.ts index 78e73e9fe1e90..be48373f056c2 100644 --- a/web/packages/teleterm/src/mainProcess/types.ts +++ b/web/packages/teleterm/src/mainProcess/types.ts @@ -276,3 +276,12 @@ export enum MainProcessIpc { export enum WindowsManagerIpc { SignalUserInterfaceReadiness = 'windows-manager-signal-user-interface-readiness', } + +/** + * A custom message to gracefully quit a process. + * It is sent to the child process with `process.send`. + * + * We need this because `process.kill('SIGTERM')` doesn't work on Windows, + * so we couldn't run any cleanup logic. + */ +export const TERMINATE_MESSAGE = 'TERMINATE_MESSAGE'; diff --git a/web/packages/teleterm/src/preload.ts b/web/packages/teleterm/src/preload.ts index c5b72720ca7a1..9a691d4ebf085 100644 --- a/web/packages/teleterm/src/preload.ts +++ b/web/packages/teleterm/src/preload.ts @@ -74,7 +74,14 @@ async function getElectronGlobals(): Promise { credentials.shared, runtimeSettings, { - noResume: mainProcessClient.configService.get('ssh.noResume').value, + ssh: { + noResume: mainProcessClient.configService.get('ssh.noResume').value, + }, + terminal: { + windowsBackend: mainProcessClient.configService.get( + 'terminal.windowsBackend' + ).value, + }, } ); const { diff --git a/web/packages/teleterm/src/services/config/appConfigSchema.ts b/web/packages/teleterm/src/services/config/appConfigSchema.ts index 91950eed522b1..bc8713886aa58 100644 --- a/web/packages/teleterm/src/services/config/appConfigSchema.ts +++ b/web/packages/teleterm/src/services/config/appConfigSchema.ts @@ -22,6 +22,9 @@ import { Platform } from 'teleterm/mainProcess/types'; import { createKeyboardShortcutSchema } from './keyboardShortcutSchema'; +// When adding a new config property, add it to the docs too +// (teleport-connect.mdx#configuration). + export type AppConfigSchema = ReturnType; export type AppConfig = z.infer; @@ -54,6 +57,12 @@ export const createAppConfigSchema = (platform: Platform) => { .max(256) .default(15) .describe('Font size for the terminal.'), + 'terminal.windowsBackend': z + .enum(['auto', 'winpty']) + .default('auto') + .describe( + '`auto` uses modern ConPTY system if available, which requires Windows 10 (19H1) or above. Set to `winpty` to use winpty even if ConPTY is available.' + ), 'usageReporting.enabled': z .boolean() .default(false) diff --git a/web/packages/teleterm/src/services/pty/fixtures/mocks.ts b/web/packages/teleterm/src/services/pty/fixtures/mocks.ts index c9e2c62e392c7..9cefda658c666 100644 --- a/web/packages/teleterm/src/services/pty/fixtures/mocks.ts +++ b/web/packages/teleterm/src/services/pty/fixtures/mocks.ts @@ -20,6 +20,7 @@ import { IPtyProcess } from 'teleterm/sharedProcess/ptyHost'; import { PtyProcessCreationStatus, PtyServiceClient, + WindowsPty, } from 'teleterm/services/pty'; export class MockPtyProcess implements IPtyProcess { @@ -29,7 +30,7 @@ export class MockPtyProcess implements IPtyProcess { resize() {} - dispose() {} + async dispose() {} onData() { return () => {}; @@ -64,10 +65,12 @@ export class MockPtyServiceClient implements PtyServiceClient { createPtyProcess(): Promise<{ process: IPtyProcess; creationStatus: PtyProcessCreationStatus; + windowsPty: WindowsPty; }> { return Promise.resolve({ process: new MockPtyProcess(), creationStatus: PtyProcessCreationStatus.Ok, + windowsPty: undefined, }); } } diff --git a/web/packages/teleterm/src/services/pty/ptyHost/buildPtyOptions.test.ts b/web/packages/teleterm/src/services/pty/ptyHost/buildPtyOptions.test.ts index b37187ed4f5cd..4f8720d3d2482 100644 --- a/web/packages/teleterm/src/services/pty/ptyHost/buildPtyOptions.test.ts +++ b/web/packages/teleterm/src/services/pty/ptyHost/buildPtyOptions.test.ts @@ -47,7 +47,7 @@ describe('getPtyProcessOptions', () => { const { env } = getPtyProcessOptions( makeRuntimeSettings(), - { noResume: false }, + { ssh: { noResume: false }, windowsPty: { useConpty: true } }, cmd, processEnv ); @@ -76,7 +76,7 @@ describe('getPtyProcessOptions', () => { const { env } = getPtyProcessOptions( makeRuntimeSettings(), - { noResume: false }, + { ssh: { noResume: false }, windowsPty: { useConpty: true } }, cmd, processEnv ); @@ -103,7 +103,7 @@ describe('getPtyProcessOptions', () => { const { args } = getPtyProcessOptions( makeRuntimeSettings(), - { noResume: true }, + { ssh: { noResume: true }, windowsPty: { useConpty: true } }, cmd, processEnv ); diff --git a/web/packages/teleterm/src/services/pty/ptyHost/buildPtyOptions.ts b/web/packages/teleterm/src/services/pty/ptyHost/buildPtyOptions.ts index 4857d3a498694..e4b34c934b782 100644 --- a/web/packages/teleterm/src/services/pty/ptyHost/buildPtyOptions.ts +++ b/web/packages/teleterm/src/services/pty/ptyHost/buildPtyOptions.ts @@ -25,8 +25,9 @@ import { assertUnreachable } from 'teleterm/ui/utils'; import { PtyCommand, PtyProcessCreationStatus, - SshOptions, TshKubeLoginCommand, + SshOptions, + WindowsPty, } from '../types'; import { @@ -34,9 +35,14 @@ import { ResolveShellEnvTimeoutError, } from './resolveShellEnv'; +type PtyOptions = { + ssh: SshOptions; + windowsPty: Pick; +}; + export async function buildPtyOptions( settings: RuntimeSettings, - sshOptions: SshOptions, + options: PtyOptions, cmd: PtyCommand ): Promise<{ processOptions: PtyProcessOptions; @@ -68,7 +74,7 @@ export async function buildPtyOptions( return { processOptions: getPtyProcessOptions( settings, - sshOptions, + options, cmd, combinedEnv ), @@ -79,10 +85,12 @@ export async function buildPtyOptions( export function getPtyProcessOptions( settings: RuntimeSettings, - sshOptions: SshOptions, + options: PtyOptions, cmd: PtyCommand, env: typeof process.env ): PtyProcessOptions { + const useConpty = options.windowsPty?.useConpty; + switch (cmd.kind) { case 'pty.shell': { // Teleport Connect bundles a tsh binary, but the user might have one already on their system. @@ -104,6 +112,7 @@ export function getPtyProcessOptions( cwd: cmd.cwd, env: { ...env, ...cmd.env }, initMessage: cmd.initMessage, + useConpty, }; } @@ -129,6 +138,7 @@ export function getPtyProcessOptions( path: settings.defaultShell, args: isWindows ? powershellCommandArgs : bashCommandArgs, env: { ...env, KUBECONFIG: getKubeConfigFilePath(cmd, settings) }, + useConpty, }; } @@ -140,7 +150,7 @@ export function getPtyProcessOptions( const args = [ `--proxy=${cmd.rootClusterId}`, 'ssh', - ...(sshOptions.noResume ? ['--no-resume'] : []), + ...(options.ssh.noResume ? ['--no-resume'] : []), '--forward-agent', loginHost, ]; @@ -149,6 +159,7 @@ export function getPtyProcessOptions( path: settings.tshd.binaryPath, args, env, + useConpty, }; } @@ -159,6 +170,7 @@ export function getPtyProcessOptions( path: cmd.path, args: cmd.args, env: { ...env, ...cmd.env }, + useConpty, }; } diff --git a/web/packages/teleterm/src/services/pty/ptyHost/ptyHostClient.ts b/web/packages/teleterm/src/services/pty/ptyHost/ptyHostClient.ts index 74c9dfd90fa29..8990bfb0a1f64 100644 --- a/web/packages/teleterm/src/services/pty/ptyHost/ptyHostClient.ts +++ b/web/packages/teleterm/src/services/pty/ptyHost/ptyHostClient.ts @@ -41,6 +41,7 @@ export function createPtyHostClient( args: ptyOptions.args, path: ptyOptions.path, env: Struct.fromJson(ptyOptions.env), + useConpty: ptyOptions.useConpty, }); if (ptyOptions.cwd) { diff --git a/web/packages/teleterm/src/services/pty/ptyHost/ptyProcess.ts b/web/packages/teleterm/src/services/pty/ptyHost/ptyProcess.ts index 8b97c017f7a93..cec0ba3d246f3 100644 --- a/web/packages/teleterm/src/services/pty/ptyHost/ptyProcess.ts +++ b/web/packages/teleterm/src/services/pty/ptyHost/ptyProcess.ts @@ -47,7 +47,7 @@ export function createPtyProcess( exchangeEventsStream.resize(columns, rows); }, - dispose(): void { + async dispose(): Promise { exchangeEventsStream.dispose(); }, diff --git a/web/packages/teleterm/src/services/pty/ptyHost/windowsPty.test.ts b/web/packages/teleterm/src/services/pty/ptyHost/windowsPty.test.ts new file mode 100644 index 0000000000000..28e89c7e26ccc --- /dev/null +++ b/web/packages/teleterm/src/services/pty/ptyHost/windowsPty.test.ts @@ -0,0 +1,61 @@ +/** + * 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 . + */ + +import { makeRuntimeSettings } from 'teleterm/mainProcess/fixtures/mocks'; + +import { getWindowsPty } from './windowsPty'; + +test.each([ + { + name: 'uses conpty on supported Windows version', + platform: 'win32' as const, + osVersion: '10.0.22621', + terminalOptions: { windowsBackend: 'auto' as const }, + expected: { useConpty: true, buildNumber: 22621 }, + }, + { + name: 'uses winpty on unsupported Windows version', + platform: 'win32' as const, + osVersion: '10.0.18308', + terminalOptions: { windowsBackend: 'auto' as const }, + expected: { useConpty: false, buildNumber: 18308 }, + }, + { + name: 'uses winpty when Windows version is supported, but conpty is disabled in options', + platform: 'win32' as const, + osVersion: '10.0.22621', + terminalOptions: { windowsBackend: 'winpty' as const }, + expected: { useConpty: false, buildNumber: 22621 }, + }, + { + name: 'undefined on non-Windows OS', + platform: 'darwin' as const, + osVersion: '23.5.0', + terminalOptions: { windowsBackend: 'auto' as const }, + expected: undefined, + }, +])('$name', ({ platform, osVersion, terminalOptions, expected }) => { + const pty = getWindowsPty( + makeRuntimeSettings({ + platform, + osVersion, + }), + terminalOptions + ); + expect(pty).toEqual(expected); +}); diff --git a/web/packages/teleterm/src/services/pty/ptyHost/windowsPty.ts b/web/packages/teleterm/src/services/pty/ptyHost/windowsPty.ts new file mode 100644 index 0000000000000..8bd69d0d9de32 --- /dev/null +++ b/web/packages/teleterm/src/services/pty/ptyHost/windowsPty.ts @@ -0,0 +1,49 @@ +/** + * 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 . + */ + +import { RuntimeSettings } from 'teleterm/mainProcess/types'; + +import { TerminalOptions, WindowsPty } from '../types'; + +export const WIN_BUILD_STABLE_CONPTY = 18309; + +export function getWindowsPty( + runtimeSettings: RuntimeSettings, + terminalOptions: TerminalOptions +): WindowsPty { + if (runtimeSettings.platform !== 'win32') { + return undefined; + } + + const buildNumber = getWindowsBuildNumber(runtimeSettings.osVersion); + const useConpty = + terminalOptions.windowsBackend === 'auto' && + buildNumber >= WIN_BUILD_STABLE_CONPTY; + return { + useConpty, + buildNumber, + }; +} + +function getWindowsBuildNumber(osVersion: string): number { + const parsedOsVersion = /(\d+)\.(\d+)\.(\d+)/g.exec(osVersion); + if (parsedOsVersion?.length === 4) { + return parseInt(parsedOsVersion[3]); + } + return 0; +} diff --git a/web/packages/teleterm/src/services/pty/ptyService.ts b/web/packages/teleterm/src/services/pty/ptyService.ts index b13c11325179f..3b82021e8868d 100644 --- a/web/packages/teleterm/src/services/pty/ptyService.ts +++ b/web/packages/teleterm/src/services/pty/ptyService.ts @@ -23,21 +23,26 @@ import { RuntimeSettings } from 'teleterm/mainProcess/types'; import { buildPtyOptions } from './ptyHost/buildPtyOptions'; import { createPtyHostClient } from './ptyHost/ptyHostClient'; import { createPtyProcess } from './ptyHost/ptyProcess'; -import { PtyServiceClient, SshOptions } from './types'; +import { PtyServiceClient, PtyOptions } from './types'; +import { getWindowsPty } from './ptyHost/windowsPty'; export function createPtyService( address: string, credentials: ChannelCredentials, runtimeSettings: RuntimeSettings, - sshOptions: SshOptions + options: PtyOptions ): PtyServiceClient { const ptyHostClient = createPtyHostClient(address, credentials); return { createPtyProcess: async command => { + const windowsPty = getWindowsPty(runtimeSettings, options.terminal); const { processOptions, creationStatus } = await buildPtyOptions( runtimeSettings, - sshOptions, + { + ssh: options.ssh, + windowsPty, + }, command ); const ptyId = await ptyHostClient.createPtyProcess(processOptions); @@ -46,6 +51,7 @@ export function createPtyService( return { process: createPtyProcess(ptyHostClient, ptyId), creationStatus, + windowsPty, }; }, }; diff --git a/web/packages/teleterm/src/services/pty/types.ts b/web/packages/teleterm/src/services/pty/types.ts index 7119a89ea993c..aaac99a224ede 100644 --- a/web/packages/teleterm/src/services/pty/types.ts +++ b/web/packages/teleterm/src/services/pty/types.ts @@ -37,9 +37,21 @@ export type PtyServiceClient = { createPtyProcess: (cmd: PtyCommand) => Promise<{ process: IPtyProcess; creationStatus: PtyProcessCreationStatus; + windowsPty: WindowsPty; }>; }; +/** + * Pty information for Windows. + * undefined for non-Windows OS. + */ +export type WindowsPty = + | { + useConpty: boolean; + buildNumber: number; + } + | undefined; + export type ShellCommand = PtyCommandBase & { kind: 'pty.shell'; cwd?: string; @@ -107,3 +119,12 @@ export type SshOptions = { */ noResume: boolean; }; + +export type TerminalOptions = { + windowsBackend: 'auto' | 'winpty'; +}; + +export type PtyOptions = { + ssh: SshOptions; + terminal: TerminalOptions; +}; diff --git a/web/packages/teleterm/src/sharedProcess/api/proto/ptyHostService.proto b/web/packages/teleterm/src/sharedProcess/api/proto/ptyHostService.proto index f9baea4f2c9fc..98a0519c61a81 100644 --- a/web/packages/teleterm/src/sharedProcess/api/proto/ptyHostService.proto +++ b/web/packages/teleterm/src/sharedProcess/api/proto/ptyHostService.proto @@ -41,6 +41,7 @@ message PtyCreate { reserved "init_command"; google.protobuf.Struct env = 7; string init_message = 8; + bool use_conpty = 9; } message PtyClientEvent { diff --git a/web/packages/teleterm/src/sharedProcess/api/protogen/ptyHostService_pb.ts b/web/packages/teleterm/src/sharedProcess/api/protogen/ptyHostService_pb.ts index cd6cd61badcea..35f043a2d4624 100644 --- a/web/packages/teleterm/src/sharedProcess/api/protogen/ptyHostService_pb.ts +++ b/web/packages/teleterm/src/sharedProcess/api/protogen/ptyHostService_pb.ts @@ -69,6 +69,10 @@ export interface PtyCreate { * @generated from protobuf field: string init_message = 8; */ initMessage: string; + /** + * @generated from protobuf field: bool use_conpty = 9; + */ + useConpty: boolean; } /** * @generated from protobuf message PtyClientEvent @@ -266,7 +270,8 @@ class PtyCreate$Type extends MessageType { { no: 4, name: "args", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ }, { no: 5, name: "cwd", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, { no: 7, name: "env", kind: "message", T: () => Struct }, - { no: 8, name: "init_message", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + { no: 8, name: "init_message", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 9, name: "use_conpty", kind: "scalar", T: 8 /*ScalarType.BOOL*/ } ]); } create(value?: PartialMessage): PtyCreate { @@ -275,6 +280,7 @@ class PtyCreate$Type extends MessageType { message.args = []; message.cwd = ""; message.initMessage = ""; + message.useConpty = false; if (value !== undefined) reflectionMergePartial(this, message, value); return message; @@ -299,6 +305,9 @@ class PtyCreate$Type extends MessageType { case /* string init_message */ 8: message.initMessage = reader.string(); break; + case /* bool use_conpty */ 9: + message.useConpty = reader.bool(); + break; default: let u = options.readUnknownField; if (u === "throw") @@ -326,6 +335,9 @@ class PtyCreate$Type extends MessageType { /* string init_message = 8; */ if (message.initMessage !== "") writer.tag(8, WireType.LengthDelimited).string(message.initMessage); + /* bool use_conpty = 9; */ + if (message.useConpty !== false) + writer.tag(9, WireType.Varint).bool(message.useConpty); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); diff --git a/web/packages/teleterm/src/sharedProcess/ptyHost/ptyEventsStreamHandler.ts b/web/packages/teleterm/src/sharedProcess/ptyHost/ptyEventsStreamHandler.ts index 0417408cc1ba7..a9aa281451131 100644 --- a/web/packages/teleterm/src/sharedProcess/ptyHost/ptyEventsStreamHandler.ts +++ b/web/packages/teleterm/src/sharedProcess/ptyHost/ptyEventsStreamHandler.ts @@ -131,16 +131,16 @@ export class PtyEventsStreamHandler { private handleStreamError(error: Error): void { this.logger.error(`stream has ended with error`, error); - this.cleanResources(); + void this.cleanResources(); } private handleStreamEnd(): void { this.logger.info(`stream has ended`); - this.cleanResources(); + void this.cleanResources(); } - private cleanResources(): void { - this.ptyProcess.dispose(); + private async cleanResources(): Promise { + await this.ptyProcess.dispose(); if (this.ptyId) { this.ptyProcesses.delete(this.ptyId); } diff --git a/web/packages/teleterm/src/sharedProcess/ptyHost/ptyHostService.ts b/web/packages/teleterm/src/sharedProcess/ptyHost/ptyHostService.ts index 651a196e5ad6d..ffb1cb524321b 100644 --- a/web/packages/teleterm/src/sharedProcess/ptyHost/ptyHostService.ts +++ b/web/packages/teleterm/src/sharedProcess/ptyHost/ptyHostService.ts @@ -27,7 +27,9 @@ import { IPtyHost } from './../api/protogen/ptyHostService_pb.grpc-server'; import { PtyCwd, PtyId } from './../api/protogen/ptyHostService_pb'; import { PtyEventsStreamHandler } from './ptyEventsStreamHandler'; -export function createPtyHostService(): IPtyHost { +export function createPtyHostService(): IPtyHost & { + dispose(): Promise; +} { const logger = new Logger('PtyHostService'); const ptyProcesses = new Map(); @@ -43,6 +45,7 @@ export function createPtyHostService(): IPtyHost { ptyId, env: Struct.toJson(call.request.env!) as Record, initMessage: ptyOptions.initMessage, + useConpty: ptyOptions.useConpty, }); ptyProcesses.set(ptyId, ptyProcess); } catch (error) { @@ -73,5 +76,12 @@ export function createPtyHostService(): IPtyHost { }); }, exchangeEvents: stream => new PtyEventsStreamHandler(stream, ptyProcesses), + dispose: async () => { + await Promise.all( + Array.from(ptyProcesses.values()).map(ptyProcess => + ptyProcess.dispose() + ) + ); + }, }; } diff --git a/web/packages/teleterm/src/sharedProcess/ptyHost/ptyProcess.test.ts b/web/packages/teleterm/src/sharedProcess/ptyHost/ptyProcess.test.ts index 18288c307d981..77d8a7dddad68 100644 --- a/web/packages/teleterm/src/sharedProcess/ptyHost/ptyProcess.test.ts +++ b/web/packages/teleterm/src/sharedProcess/ptyHost/ptyProcess.test.ts @@ -38,6 +38,7 @@ describe('PtyProcess', () => { args: [], env: { PATH: '/foo/bar' }, ptyId: '1234', + useConpty: true, }); const startErrorCb = jest.fn(); diff --git a/web/packages/teleterm/src/sharedProcess/ptyHost/ptyProcess.ts b/web/packages/teleterm/src/sharedProcess/ptyHost/ptyProcess.ts index ededa80e5ffc4..f38e1503ffacf 100644 --- a/web/packages/teleterm/src/sharedProcess/ptyHost/ptyProcess.ts +++ b/web/packages/teleterm/src/sharedProcess/ptyHost/ptyProcess.ts @@ -24,6 +24,8 @@ import { EventEmitter } from 'node:events'; import * as nodePTY from 'node-pty'; import which from 'which'; +import { wait } from 'shared/utils/wait'; + import Logger from 'teleterm/logger'; import { PtyProcessOptions, IPtyProcess } from './types'; @@ -59,6 +61,12 @@ export class PtyProcess extends EventEmitter implements IPtyProcess { * It emits TermEventEnum.StartError on error. start itself always returns a fulfilled promise. */ async start(cols: number, rows: number) { + if (process.platform === 'win32') { + this._logger.info( + this.options.useConpty ? 'ConPTY enabled' : 'ConPTY disabled' + ); + } + try { // which throws an error if the argument is not found in path. // TODO(ravicious): Remove the manual check for the existence of the executable after node-pty @@ -76,8 +84,7 @@ export class PtyProcess extends EventEmitter implements IPtyProcess { // https://unix.stackexchange.com/questions/123858 cwd: this.options.cwd || getDefaultCwd(this.options.env), env: this.options.env, - // Turn off ConPTY due to an uncaught exception being thrown when a PTY is closed. - useConpty: false, + useConpty: this.options.useConpty, }); } catch (error) { this._logger.error(error); @@ -132,10 +139,38 @@ export class PtyProcess extends EventEmitter implements IPtyProcess { } } - dispose() { + async dispose() { + if (this._disposed) { + this._logger.info(`PTY process is not running. Nothing to kill`); + return; + } + const controller = new AbortController(); + const processExit = promisifyProcessExit(this._process); + this.removeAllListeners(); - this._process?.kill(); - this._disposed = true; + this._process.kill(); + + // Wait for the process to exit. + // It's needed for ssh sessions on Windows with ConPTY enabled. + // When we didn't wait, conhost.exe processes started by node-pty + // were left running after closing the app. + // Killing a process doesn't happen immediately, but instead appears to be + // queued, so we need to give it time to execute. + // + // Although this was added specifically for Windows, + // we run the same cleanup code for all platforms. + const hasExited = await Promise.race([ + processExit.then(() => controller.abort()).then(() => true), + // timeout for killing the shared process is 5 seconds + wait(4_000, controller.signal) + .catch(() => {}) // ignore abort errors + .then(() => false), + ]); + if (hasExited) { + this._disposed = true; + } else { + this._logger.error('Failed to dispose PTY process within the timeout'); + } } onData(cb: (data: string) => void) { @@ -254,3 +289,7 @@ function getDefaultCwd(env: Record): string { return userDir || process.cwd(); } + +function promisifyProcessExit(childProcess: nodePTY.IPty): Promise { + return new Promise(resolve => childProcess.onExit(() => resolve())); +} diff --git a/web/packages/teleterm/src/sharedProcess/ptyHost/types.ts b/web/packages/teleterm/src/sharedProcess/ptyHost/types.ts index a43e82c033458..cd0f4b8bce4c6 100644 --- a/web/packages/teleterm/src/sharedProcess/ptyHost/types.ts +++ b/web/packages/teleterm/src/sharedProcess/ptyHost/types.ts @@ -22,13 +22,15 @@ export type PtyProcessOptions = { args: string[]; cwd?: string; initMessage?: string; + /** Whether to use the ConPTY system on Windows. */ + useConpty: boolean; }; export type IPtyProcess = { start(cols: number, rows: number): void; write(data: string): void; resize(cols: number, rows: number): void; - dispose(): void; + dispose(): Promise; getCwd(): Promise; getPtyId(): string; // The listener removal functions are used only on the frontend app side from the renderer process. diff --git a/web/packages/teleterm/src/sharedProcess/sharedProcess.ts b/web/packages/teleterm/src/sharedProcess/sharedProcess.ts index b4f0f5c06c861..1b9be627d8f3e 100644 --- a/web/packages/teleterm/src/sharedProcess/sharedProcess.ts +++ b/web/packages/teleterm/src/sharedProcess/sharedProcess.ts @@ -28,7 +28,7 @@ import { readGrpcCert, shouldEncryptConnection, } from 'teleterm/services/grpcCredentials'; -import { RuntimeSettings } from 'teleterm/mainProcess/types'; +import { RuntimeSettings, TERMINATE_MESSAGE } from 'teleterm/mainProcess/types'; import Logger from 'teleterm/logger'; import { ptyHostDefinition } from 'teleterm/sharedProcess/api/protogen/ptyHostService_pb.grpc-server'; @@ -74,7 +74,8 @@ async function initializeServer( } const server = new Server(); - server.addService(ptyHostDefinition, createPtyHostService()); + const ptyHostService = createPtyHostService(); + server.addService(ptyHostDefinition, ptyHostService); // grpc-js requires us to pass localhost:port for TCP connections, const grpcServerAddress = address.replace('tcp://', ''); @@ -95,8 +96,13 @@ async function initializeServer( logger.error('Could not start shared server', e); } - process.once('exit', () => { - server.forceShutdown(); + process.on('message', async message => { + if (message === TERMINATE_MESSAGE) { + new Logger('Process').info('Received terminate message, exiting'); + server.forceShutdown(); + await ptyHostService.dispose(); + process.exit(0); + } }); } diff --git a/web/packages/teleterm/src/ui/DocumentTerminal/DocumentTerminal.tsx b/web/packages/teleterm/src/ui/DocumentTerminal/DocumentTerminal.tsx index 440029df07578..15e53b53b36b6 100644 --- a/web/packages/teleterm/src/ui/DocumentTerminal/DocumentTerminal.tsx +++ b/web/packages/teleterm/src/ui/DocumentTerminal/DocumentTerminal.tsx @@ -132,6 +132,7 @@ export function DocumentTerminal(props: { unsanitizedFontFamily={unsanitizedTerminalFontFamily} fontSize={terminalFontSize} onEnterKey={attempt.data.refreshTitle} + windowsPty={attempt.data.windowsPty} /> )} diff --git a/web/packages/teleterm/src/ui/DocumentTerminal/Terminal/Terminal.tsx b/web/packages/teleterm/src/ui/DocumentTerminal/Terminal/Terminal.tsx index 4fb00273a02cb..4fb481f837b7c 100644 --- a/web/packages/teleterm/src/ui/DocumentTerminal/Terminal/Terminal.tsx +++ b/web/packages/teleterm/src/ui/DocumentTerminal/Terminal/Terminal.tsx @@ -27,6 +27,7 @@ import { makeSuccessAttempt, } from 'shared/hooks/useAsync'; +import { WindowsPty } from 'teleterm/services/pty'; import { IPtyProcess } from 'teleterm/sharedProcess/ptyHost'; import { DocumentTerminal } from 'teleterm/ui/services/workspacesService'; @@ -48,6 +49,7 @@ type TerminalProps = { unsanitizedFontFamily: string; fontSize: number; onEnterKey?(): void; + windowsPty: WindowsPty; }; export function Terminal(props: TerminalProps) { @@ -72,6 +74,7 @@ export function Terminal(props: TerminalProps) { el: refElement.current, fontSize: props.fontSize, theme: theme.colors.terminal, + windowsPty: props.windowsPty, }); // Start the PTY process. diff --git a/web/packages/teleterm/src/ui/DocumentTerminal/Terminal/ctrl.ts b/web/packages/teleterm/src/ui/DocumentTerminal/Terminal/ctrl.ts index fdae12dcad53e..1a30c9aa78a6b 100644 --- a/web/packages/teleterm/src/ui/DocumentTerminal/Terminal/ctrl.ts +++ b/web/packages/teleterm/src/ui/DocumentTerminal/Terminal/ctrl.ts @@ -16,11 +16,12 @@ * along with this program. If not, see . */ -import 'xterm/css/xterm.css'; -import { IDisposable, ITheme, Terminal } from 'xterm'; -import { FitAddon } from 'xterm-addon-fit'; +import '@xterm/xterm/css/xterm.css'; +import { IDisposable, ITheme, Terminal } from '@xterm/xterm'; +import { FitAddon } from '@xterm/addon-fit'; import { debounce } from 'shared/utils/highbar'; +import { WindowsPty } from 'teleterm/services/pty'; import { IPtyProcess } from 'teleterm/sharedProcess/ptyHost'; import Logger from 'teleterm/logger'; @@ -30,6 +31,7 @@ type Options = { el: HTMLElement; fontSize: number; theme: ITheme; + windowsPty: WindowsPty; }; export default class TtyTerminal { @@ -68,6 +70,10 @@ export default class TtyTerminal { scrollback: 5000, minimumContrastRatio: 4.5, // minimum for WCAG AA compliance theme: this.options.theme, + windowsPty: this.options.windowsPty && { + backend: this.options.windowsPty.useConpty ? 'conpty' : 'winpty', + buildNumber: this.options.windowsPty.buildNumber, + }, windowOptions: { setWinSizeChars: true, }, diff --git a/web/packages/teleterm/src/ui/DocumentTerminal/useDocumentTerminal.test.tsx b/web/packages/teleterm/src/ui/DocumentTerminal/useDocumentTerminal.test.tsx index 6aad2d0c4dceb..3280880988f4a 100644 --- a/web/packages/teleterm/src/ui/DocumentTerminal/useDocumentTerminal.test.tsx +++ b/web/packages/teleterm/src/ui/DocumentTerminal/useDocumentTerminal.test.tsx @@ -237,6 +237,7 @@ test('useDocumentTerminal shows a warning notification if the call to TerminalsS jest.spyOn(terminalsService, 'createPtyProcess').mockResolvedValue({ process: getPtyProcessMock(), creationStatus: PtyProcessCreationStatus.ResolveShellEnvTimeout, + windowsPty: undefined, }); jest.spyOn(notificationsService, 'notifyWarning'); @@ -574,6 +575,7 @@ const testSetup = ( return { process: getPtyProcessMock(), creationStatus: PtyProcessCreationStatus.Ok, + windowsPty: undefined, }; }); diff --git a/web/packages/teleterm/src/ui/DocumentTerminal/useDocumentTerminal.ts b/web/packages/teleterm/src/ui/DocumentTerminal/useDocumentTerminal.ts index 029d7df11f38d..155cc3e36c200 100644 --- a/web/packages/teleterm/src/ui/DocumentTerminal/useDocumentTerminal.ts +++ b/web/packages/teleterm/src/ui/DocumentTerminal/useDocumentTerminal.ts @@ -29,7 +29,11 @@ import { import { IPtyProcess } from 'teleterm/sharedProcess/ptyHost'; import { useWorkspaceContext } from 'teleterm/ui/Documents'; import { routing } from 'teleterm/ui/uri'; -import { PtyCommand, PtyProcessCreationStatus } from 'teleterm/services/pty'; +import { + PtyCommand, + PtyProcessCreationStatus, + WindowsPty, +} from 'teleterm/services/pty'; import { AmbiguousHostnameError } from 'teleterm/ui/services/resources'; import { retryWithRelogin } from 'teleterm/ui/utils'; import Logger from 'teleterm/logger'; @@ -72,7 +76,7 @@ export function useDocumentTerminal(doc: types.DocumentTerminal) { return () => { if (attempt.status === 'success') { - attempt.data.ptyProcess.dispose(); + void attempt.data.ptyProcess.dispose(); } }; // This cannot be run only mount. If the user has initialized a new PTY process by clicking the @@ -230,7 +234,7 @@ async function setUpPtyProcess( getClusterName() ); - const ptyProcess = await createPtyProcess(ctx, cmd); + const { process: ptyProcess, windowsPty } = await createPtyProcess(ctx, cmd); if (doc.kind === 'doc.terminal_tsh_node') { ctx.usageService.captureProtocolUse({ @@ -317,14 +321,15 @@ async function setUpPtyProcess( ptyProcess, refreshTitle, openContextMenu, + windowsPty, }; } async function createPtyProcess( ctx: IAppContext, cmd: PtyCommand -): Promise { - const { process, creationStatus } = +): Promise<{ process: IPtyProcess; windowsPty: WindowsPty }> { + const { process, creationStatus, windowsPty } = await ctx.terminalsService.createPtyProcess(cmd); if (creationStatus === PtyProcessCreationStatus.ResolveShellEnvTimeout) { @@ -336,7 +341,7 @@ async function createPtyProcess( }); } - return process; + return { process, windowsPty }; } // TODO(ravicious): Instead of creating cmd within useDocumentTerminal, make useDocumentTerminal