diff --git a/api/client/client.go b/api/client/client.go
index 297e8bb9ed273..c9a89cfdf820e 100644
--- a/api/client/client.go
+++ b/api/client/client.go
@@ -5196,3 +5196,8 @@ func (c *Client) IdentityCenterClient() identitycenterv1.IdentityCenterServiceCl
func (c *Client) ProvisioningServiceClient() provisioningv1.ProvisioningServiceClient {
return provisioningv1.NewProvisioningServiceClient(c.conn)
}
+
+// IntegrationsClient returns integrations client.
+func (c *Client) IntegrationsClient() integrationpb.IntegrationServiceClient {
+ return c.integrationsClient()
+}
diff --git a/api/gen/proto/go/teleport/integration/v1/integration_service.pb.go b/api/gen/proto/go/teleport/integration/v1/integration_service.pb.go
index 31912821bffe9..c1b233cacd289 100644
--- a/api/gen/proto/go/teleport/integration/v1/integration_service.pb.go
+++ b/api/gen/proto/go/teleport/integration/v1/integration_service.pb.go
@@ -24,6 +24,7 @@ import (
types "github.com/gravitational/teleport/api/types"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ durationpb "google.golang.org/protobuf/types/known/durationpb"
emptypb "google.golang.org/protobuf/types/known/emptypb"
reflect "reflect"
sync "sync"
@@ -494,6 +495,233 @@ func (x *GenerateAWSOIDCTokenResponse) GetToken() string {
return ""
}
+// GenerateGitHubUserCertRequest is a request to sign a client certificate used by
+// GitHub integration to authenticate with GitHub enterprise.
+type GenerateGitHubUserCertRequest struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Integration is the name of the integration;
+ Integration string `protobuf:"bytes,1,opt,name=integration,proto3" json:"integration,omitempty"`
+ // PublicKey is the public key to be signed.
+ PublicKey []byte `protobuf:"bytes,2,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
+ // UserId is the GitHub user id.
+ UserId string `protobuf:"bytes,3,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
+ // KeyId is the certificate ID, usually the Teleport username.
+ KeyId string `protobuf:"bytes,4,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty"`
+ // Ttl is the duration the certificate will be valid for.
+ Ttl *durationpb.Duration `protobuf:"bytes,5,opt,name=ttl,proto3" json:"ttl,omitempty"`
+}
+
+func (x *GenerateGitHubUserCertRequest) Reset() {
+ *x = GenerateGitHubUserCertRequest{}
+ mi := &file_teleport_integration_v1_integration_service_proto_msgTypes[9]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *GenerateGitHubUserCertRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GenerateGitHubUserCertRequest) ProtoMessage() {}
+
+func (x *GenerateGitHubUserCertRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_teleport_integration_v1_integration_service_proto_msgTypes[9]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use GenerateGitHubUserCertRequest.ProtoReflect.Descriptor instead.
+func (*GenerateGitHubUserCertRequest) Descriptor() ([]byte, []int) {
+ return file_teleport_integration_v1_integration_service_proto_rawDescGZIP(), []int{9}
+}
+
+func (x *GenerateGitHubUserCertRequest) GetIntegration() string {
+ if x != nil {
+ return x.Integration
+ }
+ return ""
+}
+
+func (x *GenerateGitHubUserCertRequest) GetPublicKey() []byte {
+ if x != nil {
+ return x.PublicKey
+ }
+ return nil
+}
+
+func (x *GenerateGitHubUserCertRequest) GetUserId() string {
+ if x != nil {
+ return x.UserId
+ }
+ return ""
+}
+
+func (x *GenerateGitHubUserCertRequest) GetKeyId() string {
+ if x != nil {
+ return x.KeyId
+ }
+ return ""
+}
+
+func (x *GenerateGitHubUserCertRequest) GetTtl() *durationpb.Duration {
+ if x != nil {
+ return x.Ttl
+ }
+ return nil
+}
+
+// GenerateGitHubUserCertResponse contains a signed certificate.
+type GenerateGitHubUserCertResponse struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // AuthorizedKey is the signed certificate.
+ AuthorizedKey []byte `protobuf:"bytes,1,opt,name=authorized_key,json=authorizedKey,proto3" json:"authorized_key,omitempty"`
+}
+
+func (x *GenerateGitHubUserCertResponse) Reset() {
+ *x = GenerateGitHubUserCertResponse{}
+ mi := &file_teleport_integration_v1_integration_service_proto_msgTypes[10]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *GenerateGitHubUserCertResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GenerateGitHubUserCertResponse) ProtoMessage() {}
+
+func (x *GenerateGitHubUserCertResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_teleport_integration_v1_integration_service_proto_msgTypes[10]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use GenerateGitHubUserCertResponse.ProtoReflect.Descriptor instead.
+func (*GenerateGitHubUserCertResponse) Descriptor() ([]byte, []int) {
+ return file_teleport_integration_v1_integration_service_proto_rawDescGZIP(), []int{10}
+}
+
+func (x *GenerateGitHubUserCertResponse) GetAuthorizedKey() []byte {
+ if x != nil {
+ return x.AuthorizedKey
+ }
+ return nil
+}
+
+// ExportIntegrationCertAuthoritiesRequest is the request to export cert
+// authorities for an integration.
+type ExportIntegrationCertAuthoritiesRequest struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Integration is the name of the integration;
+ Integration string `protobuf:"bytes,1,opt,name=integration,proto3" json:"integration,omitempty"`
+}
+
+func (x *ExportIntegrationCertAuthoritiesRequest) Reset() {
+ *x = ExportIntegrationCertAuthoritiesRequest{}
+ mi := &file_teleport_integration_v1_integration_service_proto_msgTypes[11]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *ExportIntegrationCertAuthoritiesRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ExportIntegrationCertAuthoritiesRequest) ProtoMessage() {}
+
+func (x *ExportIntegrationCertAuthoritiesRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_teleport_integration_v1_integration_service_proto_msgTypes[11]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use ExportIntegrationCertAuthoritiesRequest.ProtoReflect.Descriptor instead.
+func (*ExportIntegrationCertAuthoritiesRequest) Descriptor() ([]byte, []int) {
+ return file_teleport_integration_v1_integration_service_proto_rawDescGZIP(), []int{11}
+}
+
+func (x *ExportIntegrationCertAuthoritiesRequest) GetIntegration() string {
+ if x != nil {
+ return x.Integration
+ }
+ return ""
+}
+
+// ExportIntegrationCertAuthoritiesResponse is the response to
+// ExportIntegrationCertAuthorities.
+type ExportIntegrationCertAuthoritiesResponse struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // CertAuthorities are the CA key sets used to sign any new certificates.
+ CertAuthorities *types.CAKeySet `protobuf:"bytes,1,opt,name=cert_authorities,json=certAuthorities,proto3" json:"cert_authorities,omitempty"`
+}
+
+func (x *ExportIntegrationCertAuthoritiesResponse) Reset() {
+ *x = ExportIntegrationCertAuthoritiesResponse{}
+ mi := &file_teleport_integration_v1_integration_service_proto_msgTypes[12]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *ExportIntegrationCertAuthoritiesResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ExportIntegrationCertAuthoritiesResponse) ProtoMessage() {}
+
+func (x *ExportIntegrationCertAuthoritiesResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_teleport_integration_v1_integration_service_proto_msgTypes[12]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use ExportIntegrationCertAuthoritiesResponse.ProtoReflect.Descriptor instead.
+func (*ExportIntegrationCertAuthoritiesResponse) Descriptor() ([]byte, []int) {
+ return file_teleport_integration_v1_integration_service_proto_rawDescGZIP(), []int{12}
+}
+
+func (x *ExportIntegrationCertAuthoritiesResponse) GetCertAuthorities() *types.CAKeySet {
+ if x != nil {
+ return x.CertAuthorities
+ }
+ return nil
+}
+
var File_teleport_integration_v1_integration_service_proto protoreflect.FileDescriptor
var file_teleport_integration_v1_integration_service_proto_rawDesc = []byte{
@@ -501,7 +729,9 @@ var file_teleport_integration_v1_integration_service_proto_rawDesc = []byte{
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x12, 0x17, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e,
- 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x1a, 0x1b, 0x67, 0x6f,
+ 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x1a, 0x1e, 0x67, 0x6f,
+ 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75,
+ 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d,
0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x74, 0x65, 0x6c, 0x65, 0x70,
0x6f, 0x72, 0x74, 0x2f, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73,
@@ -547,60 +777,107 @@ var file_teleport_integration_v1_integration_service_proto_rawDesc = []byte{
0x1c, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x41, 0x57, 0x53, 0x4f, 0x49, 0x44, 0x43,
0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a,
0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f,
- 0x6b, 0x65, 0x6e, 0x32, 0xef, 0x05, 0x0a, 0x12, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74,
- 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x77, 0x0a, 0x10, 0x4c, 0x69,
- 0x73, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x30,
+ 0x6b, 0x65, 0x6e, 0x22, 0xbd, 0x01, 0x0a, 0x1d, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65,
+ 0x47, 0x69, 0x74, 0x48, 0x75, 0x62, 0x55, 0x73, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 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, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69,
+ 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69,
+ 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12,
+ 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x05, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03,
+ 0x74, 0x74, 0x6c, 0x22, 0x47, 0x0a, 0x1e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x47,
+ 0x69, 0x74, 0x48, 0x75, 0x62, 0x55, 0x73, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x52, 0x65, 0x73,
+ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x61,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x22, 0x4b, 0x0a, 0x27,
+ 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 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, 0x22, 0x66, 0x0a, 0x28, 0x45, 0x78, 0x70,
+ 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x65,
+ 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73,
+ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x10, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x0f, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x43, 0x41, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x74,
+ 0x52, 0x0f, 0x63, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65,
+ 0x73, 0x32, 0xa5, 0x08, 0x0a, 0x12, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x77, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74,
+ 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 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, 0x49, 0x6e, 0x74, 0x65, 0x67,
+ 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 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, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x74,
- 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
- 0x1a, 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, 0x4c, 0x69, 0x73, 0x74, 0x49,
- 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
- 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72,
- 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 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,
- 0x47, 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x49, 0x6e,
- 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x31, 0x12, 0x5c, 0x0a, 0x11, 0x43,
- 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
- 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, 0x43, 0x72, 0x65, 0x61, 0x74,
- 0x65, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+ 0x65, 0x12, 0x56, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x12, 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, 0x47, 0x65,
+ 0x74, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x49, 0x6e, 0x74, 0x65,
- 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x31, 0x12, 0x5c, 0x0a, 0x11, 0x55, 0x70, 0x64,
+ 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x31, 0x12, 0x5c, 0x0a, 0x11, 0x43, 0x72, 0x65,
0x61, 0x74, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 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, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x49,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49,
0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x14, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72,
- 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x31, 0x12, 0x5e, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x31, 0x12, 0x5c, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74,
0x65, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 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, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x6e, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x74,
0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
- 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
- 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x66, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74,
- 0x65, 0x41, 0x6c, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
- 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, 0x6c, 0x65, 0x74,
- 0x65, 0x41, 0x6c, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
- 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12,
- 0x83, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x41, 0x57, 0x53, 0x4f,
- 0x49, 0x44, 0x43, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x34, 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, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x41, 0x57, 0x53, 0x4f, 0x49,
- 0x44, 0x43, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35,
+ 0x14, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x56, 0x31, 0x12, 0x5e, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49,
+ 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 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, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x67,
+ 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e,
+ 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
+ 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x66, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41,
+ 0x6c, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 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, 0x6c, 0x65, 0x74, 0x65, 0x41,
+ 0x6c, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65,
+ 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x83, 0x01,
+ 0x0a, 0x14, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x41, 0x57, 0x53, 0x4f, 0x49, 0x44,
+ 0x43, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x34, 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, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x41, 0x57, 0x53, 0x4f, 0x49, 0x44, 0x43,
+ 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 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, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x41,
+ 0x57, 0x53, 0x4f, 0x49, 0x44, 0x43, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
+ 0x6e, 0x73, 0x65, 0x12, 0x89, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65,
+ 0x47, 0x69, 0x74, 0x48, 0x75, 0x62, 0x55, 0x73, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x12, 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, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74,
- 0x65, 0x41, 0x57, 0x53, 0x4f, 0x49, 0x44, 0x43, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 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,
+ 0x65, 0x47, 0x69, 0x74, 0x48, 0x75, 0x62, 0x55, 0x73, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x52,
+ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 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, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x47, 0x69, 0x74, 0x48, 0x75, 0x62, 0x55,
+ 0x73, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
+ 0xa7, 0x01, 0x0a, 0x20, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x74, 0x69, 0x65, 0x73, 0x12, 0x40, 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,
+ 0x78, 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52,
+ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x41, 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, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x43, 0x65, 0x72, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65,
+ 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 (
@@ -615,43 +892,55 @@ func file_teleport_integration_v1_integration_service_proto_rawDescGZIP() []byte
return file_teleport_integration_v1_integration_service_proto_rawDescData
}
-var file_teleport_integration_v1_integration_service_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
+var file_teleport_integration_v1_integration_service_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
var file_teleport_integration_v1_integration_service_proto_goTypes = []any{
- (*ListIntegrationsRequest)(nil), // 0: teleport.integration.v1.ListIntegrationsRequest
- (*ListIntegrationsResponse)(nil), // 1: teleport.integration.v1.ListIntegrationsResponse
- (*GetIntegrationRequest)(nil), // 2: teleport.integration.v1.GetIntegrationRequest
- (*CreateIntegrationRequest)(nil), // 3: teleport.integration.v1.CreateIntegrationRequest
- (*UpdateIntegrationRequest)(nil), // 4: teleport.integration.v1.UpdateIntegrationRequest
- (*DeleteIntegrationRequest)(nil), // 5: teleport.integration.v1.DeleteIntegrationRequest
- (*DeleteAllIntegrationsRequest)(nil), // 6: teleport.integration.v1.DeleteAllIntegrationsRequest
- (*GenerateAWSOIDCTokenRequest)(nil), // 7: teleport.integration.v1.GenerateAWSOIDCTokenRequest
- (*GenerateAWSOIDCTokenResponse)(nil), // 8: teleport.integration.v1.GenerateAWSOIDCTokenResponse
- (*types.IntegrationV1)(nil), // 9: types.IntegrationV1
- (*emptypb.Empty)(nil), // 10: google.protobuf.Empty
+ (*ListIntegrationsRequest)(nil), // 0: teleport.integration.v1.ListIntegrationsRequest
+ (*ListIntegrationsResponse)(nil), // 1: teleport.integration.v1.ListIntegrationsResponse
+ (*GetIntegrationRequest)(nil), // 2: teleport.integration.v1.GetIntegrationRequest
+ (*CreateIntegrationRequest)(nil), // 3: teleport.integration.v1.CreateIntegrationRequest
+ (*UpdateIntegrationRequest)(nil), // 4: teleport.integration.v1.UpdateIntegrationRequest
+ (*DeleteIntegrationRequest)(nil), // 5: teleport.integration.v1.DeleteIntegrationRequest
+ (*DeleteAllIntegrationsRequest)(nil), // 6: teleport.integration.v1.DeleteAllIntegrationsRequest
+ (*GenerateAWSOIDCTokenRequest)(nil), // 7: teleport.integration.v1.GenerateAWSOIDCTokenRequest
+ (*GenerateAWSOIDCTokenResponse)(nil), // 8: teleport.integration.v1.GenerateAWSOIDCTokenResponse
+ (*GenerateGitHubUserCertRequest)(nil), // 9: teleport.integration.v1.GenerateGitHubUserCertRequest
+ (*GenerateGitHubUserCertResponse)(nil), // 10: teleport.integration.v1.GenerateGitHubUserCertResponse
+ (*ExportIntegrationCertAuthoritiesRequest)(nil), // 11: teleport.integration.v1.ExportIntegrationCertAuthoritiesRequest
+ (*ExportIntegrationCertAuthoritiesResponse)(nil), // 12: teleport.integration.v1.ExportIntegrationCertAuthoritiesResponse
+ (*types.IntegrationV1)(nil), // 13: types.IntegrationV1
+ (*durationpb.Duration)(nil), // 14: google.protobuf.Duration
+ (*types.CAKeySet)(nil), // 15: types.CAKeySet
+ (*emptypb.Empty)(nil), // 16: google.protobuf.Empty
}
var file_teleport_integration_v1_integration_service_proto_depIdxs = []int32{
- 9, // 0: teleport.integration.v1.ListIntegrationsResponse.integrations:type_name -> types.IntegrationV1
- 9, // 1: teleport.integration.v1.CreateIntegrationRequest.integration:type_name -> types.IntegrationV1
- 9, // 2: teleport.integration.v1.UpdateIntegrationRequest.integration:type_name -> types.IntegrationV1
- 0, // 3: teleport.integration.v1.IntegrationService.ListIntegrations:input_type -> teleport.integration.v1.ListIntegrationsRequest
- 2, // 4: teleport.integration.v1.IntegrationService.GetIntegration:input_type -> teleport.integration.v1.GetIntegrationRequest
- 3, // 5: teleport.integration.v1.IntegrationService.CreateIntegration:input_type -> teleport.integration.v1.CreateIntegrationRequest
- 4, // 6: teleport.integration.v1.IntegrationService.UpdateIntegration:input_type -> teleport.integration.v1.UpdateIntegrationRequest
- 5, // 7: teleport.integration.v1.IntegrationService.DeleteIntegration:input_type -> teleport.integration.v1.DeleteIntegrationRequest
- 6, // 8: teleport.integration.v1.IntegrationService.DeleteAllIntegrations:input_type -> teleport.integration.v1.DeleteAllIntegrationsRequest
- 7, // 9: teleport.integration.v1.IntegrationService.GenerateAWSOIDCToken:input_type -> teleport.integration.v1.GenerateAWSOIDCTokenRequest
- 1, // 10: teleport.integration.v1.IntegrationService.ListIntegrations:output_type -> teleport.integration.v1.ListIntegrationsResponse
- 9, // 11: teleport.integration.v1.IntegrationService.GetIntegration:output_type -> types.IntegrationV1
- 9, // 12: teleport.integration.v1.IntegrationService.CreateIntegration:output_type -> types.IntegrationV1
- 9, // 13: teleport.integration.v1.IntegrationService.UpdateIntegration:output_type -> types.IntegrationV1
- 10, // 14: teleport.integration.v1.IntegrationService.DeleteIntegration:output_type -> google.protobuf.Empty
- 10, // 15: teleport.integration.v1.IntegrationService.DeleteAllIntegrations:output_type -> google.protobuf.Empty
- 8, // 16: teleport.integration.v1.IntegrationService.GenerateAWSOIDCToken:output_type -> teleport.integration.v1.GenerateAWSOIDCTokenResponse
- 10, // [10:17] is the sub-list for method output_type
- 3, // [3:10] is the sub-list for method input_type
- 3, // [3:3] is the sub-list for extension type_name
- 3, // [3:3] is the sub-list for extension extendee
- 0, // [0:3] is the sub-list for field type_name
+ 13, // 0: teleport.integration.v1.ListIntegrationsResponse.integrations:type_name -> types.IntegrationV1
+ 13, // 1: teleport.integration.v1.CreateIntegrationRequest.integration:type_name -> types.IntegrationV1
+ 13, // 2: teleport.integration.v1.UpdateIntegrationRequest.integration:type_name -> types.IntegrationV1
+ 14, // 3: teleport.integration.v1.GenerateGitHubUserCertRequest.ttl:type_name -> google.protobuf.Duration
+ 15, // 4: teleport.integration.v1.ExportIntegrationCertAuthoritiesResponse.cert_authorities:type_name -> types.CAKeySet
+ 0, // 5: teleport.integration.v1.IntegrationService.ListIntegrations:input_type -> teleport.integration.v1.ListIntegrationsRequest
+ 2, // 6: teleport.integration.v1.IntegrationService.GetIntegration:input_type -> teleport.integration.v1.GetIntegrationRequest
+ 3, // 7: teleport.integration.v1.IntegrationService.CreateIntegration:input_type -> teleport.integration.v1.CreateIntegrationRequest
+ 4, // 8: teleport.integration.v1.IntegrationService.UpdateIntegration:input_type -> teleport.integration.v1.UpdateIntegrationRequest
+ 5, // 9: teleport.integration.v1.IntegrationService.DeleteIntegration:input_type -> teleport.integration.v1.DeleteIntegrationRequest
+ 6, // 10: teleport.integration.v1.IntegrationService.DeleteAllIntegrations:input_type -> teleport.integration.v1.DeleteAllIntegrationsRequest
+ 7, // 11: teleport.integration.v1.IntegrationService.GenerateAWSOIDCToken:input_type -> teleport.integration.v1.GenerateAWSOIDCTokenRequest
+ 9, // 12: teleport.integration.v1.IntegrationService.GenerateGitHubUserCert:input_type -> teleport.integration.v1.GenerateGitHubUserCertRequest
+ 11, // 13: teleport.integration.v1.IntegrationService.ExportIntegrationCertAuthorities:input_type -> teleport.integration.v1.ExportIntegrationCertAuthoritiesRequest
+ 1, // 14: teleport.integration.v1.IntegrationService.ListIntegrations:output_type -> teleport.integration.v1.ListIntegrationsResponse
+ 13, // 15: teleport.integration.v1.IntegrationService.GetIntegration:output_type -> types.IntegrationV1
+ 13, // 16: teleport.integration.v1.IntegrationService.CreateIntegration:output_type -> types.IntegrationV1
+ 13, // 17: teleport.integration.v1.IntegrationService.UpdateIntegration:output_type -> types.IntegrationV1
+ 16, // 18: teleport.integration.v1.IntegrationService.DeleteIntegration:output_type -> google.protobuf.Empty
+ 16, // 19: teleport.integration.v1.IntegrationService.DeleteAllIntegrations:output_type -> google.protobuf.Empty
+ 8, // 20: teleport.integration.v1.IntegrationService.GenerateAWSOIDCToken:output_type -> teleport.integration.v1.GenerateAWSOIDCTokenResponse
+ 10, // 21: teleport.integration.v1.IntegrationService.GenerateGitHubUserCert:output_type -> teleport.integration.v1.GenerateGitHubUserCertResponse
+ 12, // 22: teleport.integration.v1.IntegrationService.ExportIntegrationCertAuthorities:output_type -> teleport.integration.v1.ExportIntegrationCertAuthoritiesResponse
+ 14, // [14:23] is the sub-list for method output_type
+ 5, // [5:14] is the sub-list for method input_type
+ 5, // [5:5] is the sub-list for extension type_name
+ 5, // [5:5] is the sub-list for extension extendee
+ 0, // [0:5] is the sub-list for field type_name
}
func init() { file_teleport_integration_v1_integration_service_proto_init() }
@@ -665,7 +954,7 @@ func file_teleport_integration_v1_integration_service_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_teleport_integration_v1_integration_service_proto_rawDesc,
NumEnums: 0,
- NumMessages: 9,
+ NumMessages: 13,
NumExtensions: 0,
NumServices: 1,
},
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 f88c39e9d6154..e003922829236 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
@@ -35,13 +35,15 @@ import (
const _ = grpc.SupportPackageIsVersion9
const (
- IntegrationService_ListIntegrations_FullMethodName = "/teleport.integration.v1.IntegrationService/ListIntegrations"
- IntegrationService_GetIntegration_FullMethodName = "/teleport.integration.v1.IntegrationService/GetIntegration"
- IntegrationService_CreateIntegration_FullMethodName = "/teleport.integration.v1.IntegrationService/CreateIntegration"
- IntegrationService_UpdateIntegration_FullMethodName = "/teleport.integration.v1.IntegrationService/UpdateIntegration"
- IntegrationService_DeleteIntegration_FullMethodName = "/teleport.integration.v1.IntegrationService/DeleteIntegration"
- IntegrationService_DeleteAllIntegrations_FullMethodName = "/teleport.integration.v1.IntegrationService/DeleteAllIntegrations"
- IntegrationService_GenerateAWSOIDCToken_FullMethodName = "/teleport.integration.v1.IntegrationService/GenerateAWSOIDCToken"
+ IntegrationService_ListIntegrations_FullMethodName = "/teleport.integration.v1.IntegrationService/ListIntegrations"
+ IntegrationService_GetIntegration_FullMethodName = "/teleport.integration.v1.IntegrationService/GetIntegration"
+ IntegrationService_CreateIntegration_FullMethodName = "/teleport.integration.v1.IntegrationService/CreateIntegration"
+ IntegrationService_UpdateIntegration_FullMethodName = "/teleport.integration.v1.IntegrationService/UpdateIntegration"
+ IntegrationService_DeleteIntegration_FullMethodName = "/teleport.integration.v1.IntegrationService/DeleteIntegration"
+ IntegrationService_DeleteAllIntegrations_FullMethodName = "/teleport.integration.v1.IntegrationService/DeleteAllIntegrations"
+ IntegrationService_GenerateAWSOIDCToken_FullMethodName = "/teleport.integration.v1.IntegrationService/GenerateAWSOIDCToken"
+ IntegrationService_GenerateGitHubUserCert_FullMethodName = "/teleport.integration.v1.IntegrationService/GenerateGitHubUserCert"
+ IntegrationService_ExportIntegrationCertAuthorities_FullMethodName = "/teleport.integration.v1.IntegrationService/ExportIntegrationCertAuthorities"
)
// IntegrationServiceClient is the client API for IntegrationService service.
@@ -65,6 +67,10 @@ type IntegrationServiceClient interface {
DeleteAllIntegrations(ctx context.Context, in *DeleteAllIntegrationsRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
// GenerateAWSOIDCToken generates a token to be used when executing an AWS OIDC Integration action.
GenerateAWSOIDCToken(ctx context.Context, in *GenerateAWSOIDCTokenRequest, opts ...grpc.CallOption) (*GenerateAWSOIDCTokenResponse, error)
+ // GenerateGitHubUserCert signs a SSH certificate for GitHub integration.
+ GenerateGitHubUserCert(ctx context.Context, in *GenerateGitHubUserCertRequest, opts ...grpc.CallOption) (*GenerateGitHubUserCertResponse, error)
+ // ExportIntegrationCertAuthorities exports cert authorities for an integration.
+ ExportIntegrationCertAuthorities(ctx context.Context, in *ExportIntegrationCertAuthoritiesRequest, opts ...grpc.CallOption) (*ExportIntegrationCertAuthoritiesResponse, error)
}
type integrationServiceClient struct {
@@ -145,6 +151,26 @@ func (c *integrationServiceClient) GenerateAWSOIDCToken(ctx context.Context, in
return out, nil
}
+func (c *integrationServiceClient) GenerateGitHubUserCert(ctx context.Context, in *GenerateGitHubUserCertRequest, opts ...grpc.CallOption) (*GenerateGitHubUserCertResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(GenerateGitHubUserCertResponse)
+ err := c.cc.Invoke(ctx, IntegrationService_GenerateGitHubUserCert_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *integrationServiceClient) ExportIntegrationCertAuthorities(ctx context.Context, in *ExportIntegrationCertAuthoritiesRequest, opts ...grpc.CallOption) (*ExportIntegrationCertAuthoritiesResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(ExportIntegrationCertAuthoritiesResponse)
+ err := c.cc.Invoke(ctx, IntegrationService_ExportIntegrationCertAuthorities_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
// IntegrationServiceServer is the server API for IntegrationService service.
// All implementations must embed UnimplementedIntegrationServiceServer
// for forward compatibility.
@@ -166,6 +192,10 @@ type IntegrationServiceServer interface {
DeleteAllIntegrations(context.Context, *DeleteAllIntegrationsRequest) (*emptypb.Empty, error)
// GenerateAWSOIDCToken generates a token to be used when executing an AWS OIDC Integration action.
GenerateAWSOIDCToken(context.Context, *GenerateAWSOIDCTokenRequest) (*GenerateAWSOIDCTokenResponse, error)
+ // GenerateGitHubUserCert signs a SSH certificate for GitHub integration.
+ GenerateGitHubUserCert(context.Context, *GenerateGitHubUserCertRequest) (*GenerateGitHubUserCertResponse, error)
+ // ExportIntegrationCertAuthorities exports cert authorities for an integration.
+ ExportIntegrationCertAuthorities(context.Context, *ExportIntegrationCertAuthoritiesRequest) (*ExportIntegrationCertAuthoritiesResponse, error)
mustEmbedUnimplementedIntegrationServiceServer()
}
@@ -197,6 +227,12 @@ func (UnimplementedIntegrationServiceServer) DeleteAllIntegrations(context.Conte
func (UnimplementedIntegrationServiceServer) GenerateAWSOIDCToken(context.Context, *GenerateAWSOIDCTokenRequest) (*GenerateAWSOIDCTokenResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GenerateAWSOIDCToken not implemented")
}
+func (UnimplementedIntegrationServiceServer) GenerateGitHubUserCert(context.Context, *GenerateGitHubUserCertRequest) (*GenerateGitHubUserCertResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method GenerateGitHubUserCert not implemented")
+}
+func (UnimplementedIntegrationServiceServer) ExportIntegrationCertAuthorities(context.Context, *ExportIntegrationCertAuthoritiesRequest) (*ExportIntegrationCertAuthoritiesResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method ExportIntegrationCertAuthorities not implemented")
+}
func (UnimplementedIntegrationServiceServer) mustEmbedUnimplementedIntegrationServiceServer() {}
func (UnimplementedIntegrationServiceServer) testEmbeddedByValue() {}
@@ -344,6 +380,42 @@ func _IntegrationService_GenerateAWSOIDCToken_Handler(srv interface{}, ctx conte
return interceptor(ctx, in, info, handler)
}
+func _IntegrationService_GenerateGitHubUserCert_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(GenerateGitHubUserCertRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(IntegrationServiceServer).GenerateGitHubUserCert(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: IntegrationService_GenerateGitHubUserCert_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(IntegrationServiceServer).GenerateGitHubUserCert(ctx, req.(*GenerateGitHubUserCertRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _IntegrationService_ExportIntegrationCertAuthorities_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(ExportIntegrationCertAuthoritiesRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(IntegrationServiceServer).ExportIntegrationCertAuthorities(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: IntegrationService_ExportIntegrationCertAuthorities_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(IntegrationServiceServer).ExportIntegrationCertAuthorities(ctx, req.(*ExportIntegrationCertAuthoritiesRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
// IntegrationService_ServiceDesc is the grpc.ServiceDesc for IntegrationService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@@ -379,6 +451,14 @@ var IntegrationService_ServiceDesc = grpc.ServiceDesc{
MethodName: "GenerateAWSOIDCToken",
Handler: _IntegrationService_GenerateAWSOIDCToken_Handler,
},
+ {
+ MethodName: "GenerateGitHubUserCert",
+ Handler: _IntegrationService_GenerateGitHubUserCert_Handler,
+ },
+ {
+ MethodName: "ExportIntegrationCertAuthorities",
+ Handler: _IntegrationService_ExportIntegrationCertAuthorities_Handler,
+ },
},
Streams: []grpc.StreamDesc{},
Metadata: "teleport/integration/v1/integration_service.proto",
diff --git a/api/proto/teleport/integration/v1/integration_service.proto b/api/proto/teleport/integration/v1/integration_service.proto
index 6306a204a79d7..0528f521f684e 100644
--- a/api/proto/teleport/integration/v1/integration_service.proto
+++ b/api/proto/teleport/integration/v1/integration_service.proto
@@ -16,6 +16,7 @@ syntax = "proto3";
package teleport.integration.v1;
+import "google/protobuf/duration.proto";
import "google/protobuf/empty.proto";
import "teleport/legacy/types/types.proto";
@@ -44,6 +45,12 @@ service IntegrationService {
// GenerateAWSOIDCToken generates a token to be used when executing an AWS OIDC Integration action.
rpc GenerateAWSOIDCToken(GenerateAWSOIDCTokenRequest) returns (GenerateAWSOIDCTokenResponse);
+
+ // GenerateGitHubUserCert signs a SSH certificate for GitHub integration.
+ rpc GenerateGitHubUserCert(GenerateGitHubUserCertRequest) returns (GenerateGitHubUserCertResponse);
+
+ // ExportIntegrationCertAuthorities exports cert authorities for an integration.
+ rpc ExportIntegrationCertAuthorities(ExportIntegrationCertAuthoritiesRequest) returns (ExportIntegrationCertAuthoritiesResponse);
}
// ListIntegrationsRequest is a request for a paginated list of Integrations.
@@ -111,3 +118,38 @@ message GenerateAWSOIDCTokenResponse {
// Token is the signed JWT ready to be used
string token = 1;
}
+
+// GenerateGitHubUserCertRequest is a request to sign a client certificate used by
+// GitHub integration to authenticate with GitHub enterprise.
+message GenerateGitHubUserCertRequest {
+ // Integration is the name of the integration;
+ string integration = 1;
+ // PublicKey is the public key to be signed.
+ bytes public_key = 2;
+ // UserId is the GitHub user id.
+ string user_id = 3;
+ // KeyId is the certificate ID, usually the Teleport username.
+ string key_id = 4;
+ // Ttl is the duration the certificate will be valid for.
+ google.protobuf.Duration ttl = 5;
+}
+
+// GenerateGitHubUserCertResponse contains a signed certificate.
+message GenerateGitHubUserCertResponse {
+ // AuthorizedKey is the signed certificate.
+ bytes authorized_key = 1;
+}
+
+// ExportIntegrationCertAuthoritiesRequest is the request to export cert
+// authorities for an integration.
+message ExportIntegrationCertAuthoritiesRequest {
+ // Integration is the name of the integration;
+ string integration = 1;
+}
+
+// ExportIntegrationCertAuthoritiesResponse is the response to
+// ExportIntegrationCertAuthorities.
+message ExportIntegrationCertAuthoritiesResponse {
+ // CertAuthorities are the CA key sets used to sign any new certificates.
+ types.CAKeySet cert_authorities = 1;
+}
diff --git a/lib/auth/authclient/api.go b/lib/auth/authclient/api.go
index d2e206fd8dd51..2a9d3095b4137 100644
--- a/lib/auth/authclient/api.go
+++ b/lib/auth/authclient/api.go
@@ -1242,6 +1242,9 @@ type Cache interface {
// ListAccountAssignments fetches a paginated list of IdentityCenter Account Assignments
ListAccountAssignments(context.Context, int, *pagination.PageRequestToken) ([]services.IdentityCenterAccountAssignment, pagination.NextPageToken, error)
+
+ // GetPluginStaticCredentialsByLabels will get a list of plugin static credentials resource by matching labels.
+ GetPluginStaticCredentialsByLabels(ctx context.Context, labels map[string]string) ([]types.PluginStaticCredentials, error)
}
type NodeWrapper struct {
diff --git a/lib/auth/authclient/clt.go b/lib/auth/authclient/clt.go
index 5376d6161baa3..caf56cf3b6e09 100644
--- a/lib/auth/authclient/clt.go
+++ b/lib/auth/authclient/clt.go
@@ -1892,6 +1892,9 @@ type ClientI interface {
// ProvisioningServiceClient returns provisioning service client.
ProvisioningServiceClient() provisioningv1.ProvisioningServiceClient
+ // IntegrationsClient returns integrations client.
+ IntegrationsClient() integrationv1.IntegrationServiceClient
+
// GitServerClient returns git server client.
GitServerClient() gitserverv1.GitServerServiceClient
}
diff --git a/lib/auth/integration/credentials/credentials.go b/lib/auth/integration/credentials/credentials.go
new file mode 100644
index 0000000000000..2c16a39568395
--- /dev/null
+++ b/lib/auth/integration/credentials/credentials.go
@@ -0,0 +1,107 @@
+/*
+ * 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 credentials
+
+import (
+ "context"
+ "maps"
+
+ "github.com/google/uuid"
+ "github.com/gravitational/trace"
+
+ "github.com/gravitational/teleport/api/types"
+)
+
+// Package credentials defines constants and provides helper functions for
+// integration credentials.
+
+const (
+ // LabelStaticCredentialsIntegration is the label used to store the
+ // UUID ref in the static credentials.
+ LabelStaticCredentialsIntegration = types.TeleportInternalLabelPrefix + types.KindIntegration
+ // LabelStaticCredentialsPurpose is the label used to store the purpose of
+ // the static credentials.
+ LabelStaticCredentialsPurpose = "purpose"
+
+ // PurposeGitHubSSHCA is the label value that indicates the static
+ // credentials contains the GitHub SSH CA.
+ PurposeGitHubSSHCA = "github-sshca"
+ // PurposeGitHubOAuth is the label value that indicates the static
+ // credentials contains the GitHub OAuth ID and secret.
+ PurposeGitHubOAuth = "github-oauth"
+)
+
+// NewRef creates a new PluginStaticCredentialsRef that is saved along with the
+// integration resource in the backend. The actual credentials are saved as
+// PlugStaticCredentials and can only be retrieved by the ref.
+func NewRef() *types.PluginStaticCredentialsRef {
+ return NewRefWithUUID(uuid.NewString())
+}
+
+// NewRefWithUUID creates a PluginStaticCredentialsRef with provided UUID.
+func NewRefWithUUID(uuid string) *types.PluginStaticCredentialsRef {
+ return &types.PluginStaticCredentialsRef{
+ Labels: map[string]string{
+ LabelStaticCredentialsIntegration: uuid,
+ },
+ }
+}
+
+// CopyRefLabels copies the labels from the Ref to the actual credentials so the
+// credentials can be retrieved using the same labels.
+func CopyRefLabels(cred types.PluginStaticCredentials, ref *types.PluginStaticCredentialsRef) {
+ labels := cred.GetStaticLabels()
+ if labels == nil {
+ labels = make(map[string]string)
+ }
+ maps.Copy(labels, ref.Labels)
+
+ cred.SetStaticLabels(labels)
+}
+
+// ByLabelsGetter defines an interface to retrieve credentials by labels.
+type ByLabelsGetter interface {
+ // GetPluginStaticCredentialsByLabels will get a list of plugin static credentials resource by matching labels.
+ GetPluginStaticCredentialsByLabels(ctx context.Context, labels map[string]string) ([]types.PluginStaticCredentials, error)
+}
+
+// GetByPurpose retrieves a credentials based on the provided purpose.
+func GetByPurpose(ctx context.Context, ref *types.PluginStaticCredentialsRef, purpose string, getter ByLabelsGetter) (types.PluginStaticCredentials, error) {
+ if ref == nil {
+ return nil, trace.BadParameter("missing credentials ref")
+ }
+ labels := ref.Labels
+ if len(labels) == 0 {
+ return nil, trace.BadParameter("missing labels from credentials ref")
+ }
+ labels[LabelStaticCredentialsPurpose] = purpose
+
+ creds, err := getter.GetPluginStaticCredentialsByLabels(ctx, labels)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ switch len(creds) {
+ case 0:
+ return nil, trace.NotFound("%v credentials not found", purpose)
+ case 1:
+ return creds[0], nil
+ default:
+ return nil, trace.CompareFailed("expecting one plugin static credentials but got %v", len(creds))
+ }
+}
diff --git a/lib/auth/integration/credentials/credentials_test.go b/lib/auth/integration/credentials/credentials_test.go
new file mode 100644
index 0000000000000..03cc55c345d6e
--- /dev/null
+++ b/lib/auth/integration/credentials/credentials_test.go
@@ -0,0 +1,139 @@
+/*
+ * 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 credentials
+
+import (
+ "context"
+ "fmt"
+ "maps"
+ "testing"
+
+ "github.com/google/uuid"
+ "github.com/gravitational/trace"
+ "github.com/stretchr/testify/mock"
+ "github.com/stretchr/testify/require"
+
+ "github.com/gravitational/teleport/api/types"
+)
+
+type mockByLabelsGetter struct {
+ mock.Mock
+}
+
+func (m *mockByLabelsGetter) GetPluginStaticCredentialsByLabels(_ context.Context, labels map[string]string) ([]types.PluginStaticCredentials, error) {
+ args := m.Called(labels)
+ creds, ok := args.Get(0).([]types.PluginStaticCredentials)
+ if ok {
+ return creds, args.Error(1)
+ }
+ return nil, args.Error(1)
+}
+
+func mustMakeCred(t *testing.T, labels map[string]string) types.PluginStaticCredentials {
+ t.Helper()
+ cred, err := types.NewPluginStaticCredentials(
+ types.Metadata{
+ Name: uuid.NewString(),
+ Labels: labels,
+ },
+ types.PluginStaticCredentialsSpecV1{
+ Credentials: &types.PluginStaticCredentialsSpecV1_APIToken{
+ APIToken: "token",
+ },
+ },
+ )
+ require.NoError(t, err)
+ return cred
+}
+
+func TestGetByPurpose(t *testing.T) {
+ ref := NewRef()
+ purpose := "test-found"
+ labels := map[string]string{LabelStaticCredentialsPurpose: purpose}
+ maps.Copy(labels, ref.Labels)
+ cred := mustMakeCred(t, labels)
+
+ tests := []struct {
+ name string
+ ref *types.PluginStaticCredentialsRef
+ setupMock func(m *mockByLabelsGetter)
+ wantError func(error) bool
+ wantCred types.PluginStaticCredentials
+ }{
+ {
+ name: "nil ref",
+ ref: nil,
+ wantError: trace.IsBadParameter,
+ },
+ {
+ name: "success",
+ ref: ref,
+ setupMock: func(m *mockByLabelsGetter) {
+ m.On("GetPluginStaticCredentialsByLabels", labels).
+ Return([]types.PluginStaticCredentials{cred}, nil)
+ },
+ wantCred: cred,
+ },
+ {
+ name: "no creds found",
+ ref: ref,
+ setupMock: func(m *mockByLabelsGetter) {
+ m.On("GetPluginStaticCredentialsByLabels", labels).
+ Return([]types.PluginStaticCredentials{}, nil)
+ },
+ wantError: trace.IsNotFound,
+ },
+ {
+ name: "too mandy creds found",
+ ref: ref,
+ setupMock: func(m *mockByLabelsGetter) {
+ m.On("GetPluginStaticCredentialsByLabels", labels).
+ Return([]types.PluginStaticCredentials{cred, mustMakeCred(t, labels)}, nil)
+ },
+ wantError: trace.IsCompareFailed,
+ },
+ {
+ name: "backend issue",
+ ref: ref,
+ setupMock: func(m *mockByLabelsGetter) {
+ m.On("GetPluginStaticCredentialsByLabels", labels).
+ Return(nil, trace.ConnectionProblem(fmt.Errorf("backend"), "problem"))
+ },
+ wantError: trace.IsConnectionProblem,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ m := &mockByLabelsGetter{}
+ if test.setupMock != nil {
+ test.setupMock(m)
+ }
+
+ cred, err := GetByPurpose(context.Background(), test.ref, purpose, m)
+ if test.wantError != nil {
+ require.True(t, test.wantError(err))
+ return
+ }
+
+ require.NoError(t, err)
+ require.Equal(t, test.wantCred, cred)
+ })
+ }
+}
diff --git a/lib/auth/integration/integrationv1/credentials.go b/lib/auth/integration/integrationv1/credentials.go
index 7ea4935461b42..4d6cb888d2012 100644
--- a/lib/auth/integration/integrationv1/credentials.go
+++ b/lib/auth/integration/integrationv1/credentials.go
@@ -20,47 +20,42 @@ package integrationv1
import (
"context"
- "maps"
"github.com/google/uuid"
"github.com/gravitational/trace"
+ integrationpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1"
"github.com/gravitational/teleport/api/types"
+ "github.com/gravitational/teleport/lib/auth/integration/credentials"
"github.com/gravitational/teleport/lib/cryptosuites"
)
-const (
- // labelStaticCredentialsIntegration is the label used to store the
- // UUID ref in the static credentials.
- labelStaticCredentialsIntegration = types.TeleportInternalLabelPrefix + types.KindIntegration
- // labelStaticCredentialsPurpose is the label used to store the purpose of
- // the static credentials.
- labelStaticCredentialsPurpose = "purpose"
-
- // purposeGitHubSSHCA is the label value that indicates the static
- // credentials contains the GitHub SSH CA.
- purposeGitHubSSHCA = "github-sshca"
- // purposeGitHubOAuth is the label value that indicates the static
- // credentials contains the GitHub OAuth ID and secret.
- purposeGitHubOAuth = "github-oauth"
-)
-
-func newStaticCredentialsRef(uuid string) *types.PluginStaticCredentialsRef {
- return &types.PluginStaticCredentialsRef{
- Labels: map[string]string{
- labelStaticCredentialsIntegration: uuid,
- },
+// ExportIntegrationCertAuthorities exports cert authorities for an integration.
+func (s *Service) ExportIntegrationCertAuthorities(ctx context.Context, in *integrationpb.ExportIntegrationCertAuthoritiesRequest) (*integrationpb.ExportIntegrationCertAuthoritiesResponse, error) {
+ authCtx, err := s.authorizer.Authorize(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
}
-}
-
-func copyRefLabels(cred types.PluginStaticCredentials, ref *types.PluginStaticCredentialsRef) {
- labels := cred.GetStaticLabels()
- if labels == nil {
- labels = make(map[string]string)
+ if err := authCtx.CheckAccessToKind(types.KindIntegration, types.VerbRead); err != nil {
+ return nil, trace.Wrap(err)
+ }
+ ig, err := s.cache.GetIntegration(ctx, in.Integration)
+ if err != nil {
+ return nil, trace.Wrap(err)
}
- maps.Copy(labels, ref.Labels)
- cred.SetStaticLabels(labels)
+ // Currently only public keys are exported.
+ switch ig.GetSubKind() {
+ case types.IntegrationSubKindGitHub:
+ caKeySet, err := s.getGitHubCertAuthorities(ctx, ig)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ caKeySetWithoutSecerts := caKeySet.WithoutSecrets()
+ return &integrationpb.ExportIntegrationCertAuthoritiesResponse{CertAuthorities: &caKeySetWithoutSecerts}, nil
+ default:
+ return nil, trace.BadParameter("unsupported for integration subkind %v", ig.GetSubKind())
+ }
}
func buildGitHubOAuthCredentials(ig types.Integration) (*types.PluginStaticCredentialsV1, error) {
@@ -73,7 +68,7 @@ func buildGitHubOAuthCredentials(ig types.Integration) (*types.PluginStaticCrede
Metadata: types.Metadata{
Name: uuid.NewString(),
Labels: map[string]string{
- labelStaticCredentialsPurpose: purposeGitHubOAuth,
+ credentials.LabelStaticCredentialsPurpose: credentials.PurposeGitHubOAuth,
},
},
},
@@ -98,7 +93,7 @@ func (s *Service) newGitHubSSHCA(ctx context.Context) (*types.PluginStaticCreden
Metadata: types.Metadata{
Name: uuid.NewString(),
Labels: map[string]string{
- labelStaticCredentialsPurpose: purposeGitHubSSHCA,
+ credentials.LabelStaticCredentialsPurpose: credentials.PurposeGitHubSSHCA,
},
},
},
@@ -132,11 +127,11 @@ func (s *Service) createGitHubCredentials(ctx context.Context, ig types.Integrat
}
func (s *Service) createStaticCredentials(ctx context.Context, ig types.Integration, creds ...types.PluginStaticCredentials) error {
- ref := newStaticCredentialsRef(uuid.NewString())
+ ref := credentials.NewRef()
for _, cred := range creds {
s.logger.DebugContext(ctx, "Creating static credentials", "integration", ig.GetName(), "labels", cred.GetStaticLabels())
- copyRefLabels(cred, ref)
+ credentials.CopyRefLabels(cred, ref)
if err := s.backend.CreatePluginStaticCredentials(ctx, cred); err != nil {
return trace.Wrap(err)
}
@@ -185,7 +180,7 @@ func (s *Service) updateStaticCredentials(ctx context.Context, ig types.Integrat
s.logger.DebugContext(ctx, "Updating static credentials", "integration", ig.GetName(), "labels", cred.GetStaticLabels())
// Use same labels to find existing credentials.
- copyRefLabels(cred, ref)
+ credentials.CopyRefLabels(cred, ref)
oldCreds, err := s.backend.GetPluginStaticCredentialsByLabels(ctx, cred.GetStaticLabels())
if err != nil {
return trace.Wrap(err)
@@ -226,3 +221,29 @@ func (s *Service) removeStaticCredentials(ctx context.Context, ig types.Integrat
}
return trace.NewAggregate(errors...)
}
+
+func (s *Service) getStaticCredentialsWithPurpose(ctx context.Context, ig types.Integration, purpose string) (types.PluginStaticCredentials, error) {
+ if ig.GetCredentials() == nil {
+ return nil, trace.BadParameter("missing credentials")
+ }
+
+ return credentials.GetByPurpose(ctx, ig.GetCredentials().GetStaticCredentialsRef(), purpose, s.cache)
+}
+
+func (s *Service) getGitHubCertAuthorities(ctx context.Context, ig types.Integration) (*types.CAKeySet, error) {
+ if ig.GetSubKind() != types.IntegrationSubKindGitHub {
+ return nil, trace.BadParameter("integration is not a GitHub integration")
+ }
+ creds, err := s.getStaticCredentialsWithPurpose(ctx, ig, credentials.PurposeGitHubSSHCA)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ cas := creds.GetSSHCertAuthorities()
+ if len(cas) == 0 {
+ return nil, trace.BadParameter("missing SSH cert authorities from plugin static credentials")
+ }
+ return &types.CAKeySet{
+ SSH: cas,
+ }, nil
+}
diff --git a/lib/auth/integration/integrationv1/credentials_test.go b/lib/auth/integration/integrationv1/credentials_test.go
new file mode 100644
index 0000000000000..6e44eafe0a6cb
--- /dev/null
+++ b/lib/auth/integration/integrationv1/credentials_test.go
@@ -0,0 +1,125 @@
+/*
+ * 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 integrationv1
+
+import (
+ "context"
+ "testing"
+
+ "github.com/gravitational/trace"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ integrationpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1"
+ "github.com/gravitational/teleport/api/types"
+ "github.com/gravitational/teleport/lib/authz"
+)
+
+func TestExportIntegrationCertAuthorities(t *testing.T) {
+ t.Parallel()
+
+ ca := newCertAuthority(t, types.HostCA, "test-cluster")
+ ctx, localClient, resourceSvc := initSvc(t, ca, ca.GetClusterName(), "127.0.0.1")
+
+ githubIntegration, err := newGitHubIntegration("github-my-org", "id", "secret")
+ require.NoError(t, err)
+
+ oidcIntegration, err := types.NewIntegrationAWSOIDC(
+ types.Metadata{Name: "aws-oidc"},
+ &types.AWSOIDCIntegrationSpecV1{
+ RoleARN: "arn:aws:iam::123456789012:role/OpsTeam",
+ },
+ )
+ require.NoError(t, err)
+
+ adminCtx := authz.ContextWithUser(ctx, authz.BuiltinRole{
+ Role: types.RoleAdmin,
+ Username: string(types.RoleAdmin),
+ })
+
+ _, err = resourceSvc.CreateIntegration(adminCtx, &integrationpb.CreateIntegrationRequest{Integration: githubIntegration})
+ require.NoError(t, err)
+ _, err = resourceSvc.CreateIntegration(adminCtx, &integrationpb.CreateIntegrationRequest{Integration: oidcIntegration})
+ require.NoError(t, err)
+
+ tests := []struct {
+ name string
+ integration string
+ identity context.Context
+ check func(*testing.T, *integrationpb.ExportIntegrationCertAuthoritiesResponse, error)
+ }{
+ {
+ name: "success",
+ integration: githubIntegration.GetName(),
+ identity: adminCtx,
+ check: func(t *testing.T, resp *integrationpb.ExportIntegrationCertAuthoritiesResponse, err error) {
+ t.Helper()
+ require.NoError(t, err)
+ require.NotNil(t, resp)
+ require.NotNil(t, resp.CertAuthorities)
+ require.Len(t, resp.CertAuthorities.SSH, 1)
+ require.NotNil(t, resp.CertAuthorities.SSH[0])
+ assert.NotEmpty(t, resp.CertAuthorities.SSH[0].PublicKey)
+ assert.Empty(t, resp.CertAuthorities.SSH[0].PrivateKey)
+ },
+ },
+ {
+ name: "not found",
+ integration: "not-found",
+ identity: adminCtx,
+ check: func(t *testing.T, resp *integrationpb.ExportIntegrationCertAuthoritiesResponse, err error) {
+ t.Helper()
+ require.Nil(t, resp)
+ require.Error(t, err)
+ require.True(t, trace.IsNotFound(err))
+ },
+ },
+ {
+ name: "not allowed",
+ integration: githubIntegration.GetName(),
+ identity: authorizerForDummyUser(t, ctx, types.RoleSpecV6{}, localClient),
+ check: func(t *testing.T, resp *integrationpb.ExportIntegrationCertAuthoritiesResponse, err error) {
+ t.Helper()
+ require.Nil(t, resp)
+ require.Error(t, err)
+ require.True(t, trace.IsAccessDenied(err))
+ },
+ },
+ {
+ name: "not supported",
+ integration: oidcIntegration.GetName(),
+ identity: adminCtx,
+ check: func(t *testing.T, resp *integrationpb.ExportIntegrationCertAuthoritiesResponse, err error) {
+ t.Helper()
+ require.Nil(t, resp)
+ require.Error(t, err)
+ require.True(t, trace.IsBadParameter(err))
+ },
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ resp, err := resourceSvc.ExportIntegrationCertAuthorities(test.identity, &integrationpb.ExportIntegrationCertAuthoritiesRequest{
+ Integration: test.integration,
+ })
+ test.check(t, resp, err)
+ })
+ }
+}
diff --git a/lib/auth/integration/integrationv1/github.go b/lib/auth/integration/integrationv1/github.go
new file mode 100644
index 0000000000000..4283132b2d543
--- /dev/null
+++ b/lib/auth/integration/integrationv1/github.go
@@ -0,0 +1,103 @@
+/*
+ * 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 integrationv1
+
+import (
+ "context"
+ "crypto/rand"
+ "time"
+
+ "github.com/gravitational/trace"
+ "golang.org/x/crypto/ssh"
+
+ integrationpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1"
+ "github.com/gravitational/teleport/api/types"
+ "github.com/gravitational/teleport/lib/authz"
+)
+
+// GenerateGitHubUserCert signs a SSH certificate for GitHub integration.
+func (s *Service) GenerateGitHubUserCert(ctx context.Context, in *integrationpb.GenerateGitHubUserCertRequest) (*integrationpb.GenerateGitHubUserCertResponse, error) {
+ authCtx, err := s.authorizer.Authorize(ctx)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ if !authz.HasBuiltinRole(*authCtx, string(types.RoleProxy)) {
+ return nil, trace.AccessDenied("GenerateGitHubUserCert is only available to proxy services")
+ }
+
+ cert, err := s.prepareGitHubCert(in)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ caSigner, err := s.getGitHubSigner(ctx, in.Integration)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ if err := cert.SignCert(rand.Reader, caSigner); err != nil {
+ return nil, trace.Wrap(err)
+ }
+ return &integrationpb.GenerateGitHubUserCertResponse{
+ AuthorizedKey: ssh.MarshalAuthorizedKey(cert),
+ }, nil
+}
+
+func (s *Service) prepareGitHubCert(in *integrationpb.GenerateGitHubUserCertRequest) (*ssh.Certificate, error) {
+ if in.UserId == "" {
+ return nil, trace.BadParameter("missing UserId for GenerateGitHubUserCert")
+ }
+ if in.KeyId == "" {
+ return nil, trace.BadParameter("missing KeyId for GenerateGitHubUserCert")
+ }
+ key, _, _, _, err := ssh.ParseAuthorizedKey(in.PublicKey)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ // Sign with user ID set in id@github.com extension.
+ // https://docs.github.com/en/enterprise-cloud@latest/organizations/managing-git-access-to-your-organizations-repositories/about-ssh-certificate-authorities
+ now := s.clock.Now()
+ cert := &ssh.Certificate{
+ Key: key,
+ CertType: ssh.UserCert,
+ KeyId: in.KeyId,
+ ValidAfter: uint64(now.Add(-time.Minute).Unix()),
+ ValidBefore: uint64(now.Add(in.Ttl.AsDuration()).Unix()),
+ Permissions: ssh.Permissions{
+ Extensions: map[string]string{
+ "id@github.com": in.UserId,
+ },
+ },
+ }
+ return cert, nil
+}
+
+func (s *Service) getGitHubSigner(ctx context.Context, integration string) (ssh.Signer, error) {
+ ig, err := s.cache.GetIntegration(ctx, integration)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ caKeySet, err := s.getGitHubCertAuthorities(ctx, ig)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ caSigner, err := s.keyStoreManager.GetSSHSignerFromKeySet(ctx, *caKeySet)
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ return caSigner, nil
+}
diff --git a/lib/auth/integration/integrationv1/github_test.go b/lib/auth/integration/integrationv1/github_test.go
new file mode 100644
index 0000000000000..44fccf55ccb4a
--- /dev/null
+++ b/lib/auth/integration/integrationv1/github_test.go
@@ -0,0 +1,82 @@
+/*
+ * 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 integrationv1
+
+import (
+ "testing"
+ "time"
+
+ "github.com/gravitational/trace"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "golang.org/x/crypto/ssh"
+ "google.golang.org/protobuf/types/known/durationpb"
+
+ integrationpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1"
+ "github.com/gravitational/teleport/api/types"
+ "github.com/gravitational/teleport/lib/authz"
+ "github.com/gravitational/teleport/lib/cryptosuites"
+)
+
+func TestGenerateGitHubUserCert(t *testing.T) {
+ t.Parallel()
+
+ ca := newCertAuthority(t, types.HostCA, "test-cluster")
+ ctx, _, resourceSvc := initSvc(t, ca, ca.GetClusterName(), "127.0.0.1.nip.io")
+
+ githubIntegration, err := newGitHubIntegration("github-my-org", "id", "secret")
+ require.NoError(t, err)
+
+ adminCtx := authz.ContextWithUser(ctx, authz.BuiltinRole{
+ Role: types.RoleAdmin,
+ Username: string(types.RoleAdmin),
+ })
+ _, err = resourceSvc.CreateIntegration(adminCtx, &integrationpb.CreateIntegrationRequest{Integration: githubIntegration})
+ require.NoError(t, err)
+
+ key, err := cryptosuites.GeneratePrivateKeyWithAlgorithm(cryptosuites.Ed25519)
+ require.NoError(t, err)
+
+ req := &integrationpb.GenerateGitHubUserCertRequest{
+ Integration: "github-my-org",
+ PublicKey: key.MarshalSSHPublicKey(),
+ UserId: "1122334455",
+ KeyId: "alice",
+ Ttl: durationpb.New(time.Minute),
+ }
+
+ // Admin users cannot generate certs.
+ _, err = resourceSvc.GenerateGitHubUserCert(adminCtx, req)
+ require.True(t, trace.IsAccessDenied(err))
+
+ // Call as Proxy.
+ proxyCtx := authz.ContextWithUser(ctx, authz.BuiltinRole{
+ Role: types.RoleProxy,
+ Username: string(types.RoleProxy),
+ })
+ resp, err := resourceSvc.GenerateGitHubUserCert(proxyCtx, req)
+ require.NoError(t, err)
+ authorizedKey, _, _, _, err := ssh.ParseAuthorizedKey(resp.AuthorizedKey)
+ require.NoError(t, err)
+ sshCert, ok := authorizedKey.(*ssh.Certificate)
+ require.True(t, ok)
+
+ assert.Equal(t, "alice", sshCert.KeyId)
+ assert.Equal(t, map[string]string{"id@github.com": "1122334455"}, sshCert.Permissions.Extensions)
+}
diff --git a/lib/auth/integration/integrationv1/service.go b/lib/auth/integration/integrationv1/service.go
index 1587ed1135950..2bb00fe4543b1 100644
--- a/lib/auth/integration/integrationv1/service.go
+++ b/lib/auth/integration/integrationv1/service.go
@@ -53,6 +53,9 @@ type Cache interface {
// IntegrationsGetter defines methods to access Integration resources.
services.IntegrationsGetter
+
+ // GetPluginStaticCredentialsByLabels will get a list of plugin static credentials resource by matching labels.
+ GetPluginStaticCredentialsByLabels(ctx context.Context, labels map[string]string) ([]types.PluginStaticCredentials, error)
}
// KeyStoreManager defines methods to get signers using the server's keystore.
diff --git a/lib/auth/integration/integrationv1/service_test.go b/lib/auth/integration/integrationv1/service_test.go
index 6bf73b4897157..a8cfc66e4e45e 100644
--- a/lib/auth/integration/integrationv1/service_test.go
+++ b/lib/auth/integration/integrationv1/service_test.go
@@ -30,6 +30,7 @@ import (
integrationpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/types/externalauditstorage"
+ "github.com/gravitational/teleport/lib/auth/integration/credentials"
"github.com/gravitational/teleport/lib/auth/keystore"
"github.com/gravitational/teleport/lib/auth/testauthority"
"github.com/gravitational/teleport/lib/authz"
@@ -63,32 +64,6 @@ func TestIntegrationCRUD(t *testing.T) {
return ig
}
- newGitHubIntegration := func(name, id, secret string) (*types.IntegrationV1, error) {
- ig, err := types.NewIntegrationGitHub(
- types.Metadata{
- Name: name,
- },
- &types.GitHubIntegrationSpecV1{
- Organization: "my-org",
- },
- )
- if err != nil {
- return nil, trace.Wrap(err)
- }
-
- if secret != "" {
- ig.SetCredentials(&types.PluginCredentialsV1{
- Credentials: &types.PluginCredentialsV1_IdSecret{
- IdSecret: &types.PluginIdSecretCredential{
- Id: id,
- Secret: secret,
- },
- },
- })
- }
- return ig, nil
- }
-
tt := []struct {
Name string
Role types.RoleSpecV6
@@ -446,7 +421,7 @@ func TestIntegrationCRUD(t *testing.T) {
refUUID := uuid.NewString()
ig.SetCredentials(&types.PluginCredentialsV1{
Credentials: &types.PluginCredentialsV1_StaticCredentialsRef{
- StaticCredentialsRef: newStaticCredentialsRef(refUUID),
+ StaticCredentialsRef: credentials.NewRefWithUUID(refUUID),
},
})
@@ -459,8 +434,8 @@ func TestIntegrationCRUD(t *testing.T) {
Metadata: types.Metadata{
Name: igName,
Labels: map[string]string{
- labelStaticCredentialsIntegration: refUUID,
- labelStaticCredentialsPurpose: "test",
+ credentials.LabelStaticCredentialsIntegration: refUUID,
+ credentials.LabelStaticCredentialsPurpose: "test",
},
},
},
@@ -652,7 +627,8 @@ func initSvc(t *testing.T, ca types.CertAuthority, clusterName string, proxyPubl
PublicAddrs: []string{proxyPublicAddr},
}},
},
- IntegrationsService: *cacheResourceService,
+ IntegrationsService: *cacheResourceService,
+ PluginStaticCredentialsService: localCredService,
}
resourceSvc, err := NewService(&ServiceConfig{
@@ -689,6 +665,7 @@ type mockCache struct {
returnErr error
local.IntegrationsService
+ *local.PluginStaticCredentialsService
}
func (m *mockCache) GetProxies() ([]types.Server, error) {
@@ -769,6 +746,32 @@ func newPlugin(t *testing.T, integrationName string) *types.PluginV1 {
}
}
+func newGitHubIntegration(name, id, secret string) (*types.IntegrationV1, error) {
+ ig, err := types.NewIntegrationGitHub(
+ types.Metadata{
+ Name: name,
+ },
+ &types.GitHubIntegrationSpecV1{
+ Organization: "my-org",
+ },
+ )
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+
+ if secret != "" {
+ ig.SetCredentials(&types.PluginCredentialsV1{
+ Credentials: &types.PluginCredentialsV1_IdSecret{
+ IdSecret: &types.PluginIdSecretCredential{
+ Id: id,
+ Secret: secret,
+ },
+ },
+ })
+ }
+ return ig, nil
+}
+
func mustFindGitHubCredentials(t *testing.T, localClient Backend, igName, wantId, wantSecret string) {
t.Helper()
diff --git a/lib/cache/plugin_static_credentials.go b/lib/cache/plugin_static_credentials.go
index 6fc38713208ca..a756eb419ce35 100644
--- a/lib/cache/plugin_static_credentials.go
+++ b/lib/cache/plugin_static_credentials.go
@@ -58,7 +58,7 @@ var _ executor[types.PluginStaticCredentials, pluginStaticCredentialsGetter] = p
type pluginStaticCredentialsExecutor struct{}
func (pluginStaticCredentialsExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.PluginStaticCredentials, error) {
- return cache.pluginStaticCredentialsCache.GetAllPluginStaticCredentials(ctx)
+ return cache.PluginStaticCredentials.GetAllPluginStaticCredentials(ctx)
}
func (pluginStaticCredentialsExecutor) upsert(ctx context.Context, cache *Cache, resource types.PluginStaticCredentials) error {
diff --git a/lib/client/ca_export.go b/lib/client/ca_export.go
index d8f648819e4b8..6a1aefddb058d 100644
--- a/lib/client/ca_export.go
+++ b/lib/client/ca_export.go
@@ -22,12 +22,15 @@ import (
"context"
"encoding/pem"
"errors"
+ "fmt"
+ "log/slog"
"strings"
"time"
"github.com/gravitational/trace"
apidefaults "github.com/gravitational/teleport/api/defaults"
+ integrationpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1"
"github.com/gravitational/teleport/api/mfa"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/auth/authclient"
@@ -49,6 +52,23 @@ type ExportAuthoritiesRequest struct {
AuthType string
ExportAuthorityFingerprint string
UseCompatVersion bool
+ Integration string
+}
+
+func (r *ExportAuthoritiesRequest) shouldExportIntegration(ctx context.Context) (bool, error) {
+ switch r.AuthType {
+ case "github":
+ if r.Integration == "" {
+ return false, trace.BadParameter("integration name must be provided for %q CAs", r.AuthType)
+ }
+ return true, nil
+ default:
+ if r.Integration != "" {
+ r.Integration = ""
+ slog.DebugContext(ctx, "Integration name is ignored for non-integration CAs")
+ }
+ return false, nil
+ }
}
// ExportAuthorities returns the list of authorities in OpenSSH compatible formats as a string.
@@ -76,12 +96,22 @@ type ExportAuthoritiesRequest struct {
// > @cert-authority *.cluster-a ssh-rsa AAA... type=host
// URL encoding is used to pass the CA type and allowed logins into the comment field.
func ExportAuthorities(ctx context.Context, client authclient.ClientI, req ExportAuthoritiesRequest) (string, error) {
+ if isIntegration, err := req.shouldExportIntegration(ctx); err != nil {
+ return "", trace.Wrap(err)
+ } else if isIntegration {
+ return exportAuthForIntegration(ctx, client, req)
+ }
return exportAuth(ctx, client, req, false /* exportSecrets */)
}
// ExportAuthoritiesSecrets exports the Authority Certificate secrets (private keys).
// See ExportAuthorities for more information.
func ExportAuthoritiesSecrets(ctx context.Context, client authclient.ClientI, req ExportAuthoritiesRequest) (string, error) {
+ if isIntegration, err := req.shouldExportIntegration(ctx); err != nil {
+ return "", trace.Wrap(err)
+ } else if isIntegration {
+ return "", trace.NotImplemented("export with secrets is not supported for %q CAs", req.AuthType)
+ }
return exportAuth(ctx, client, req, true /* exportSecrets */)
}
@@ -344,3 +374,49 @@ func hostCAFormat(ca types.CertAuthority, keyBytes []byte, client authclient.Cli
},
})
}
+
+func exportAuthForIntegration(ctx context.Context, client authclient.ClientI, req ExportAuthoritiesRequest) (string, error) {
+ switch req.AuthType {
+ case "github":
+ keySet, err := fetchIntegrationCAKeySet(ctx, client, req.Integration)
+ if err != nil {
+ return "", trace.Wrap(err)
+ }
+ ret, err := exportGitHubCAs(keySet, req)
+ if err != nil {
+ return "", trace.Wrap(err)
+ }
+ return ret, nil
+
+ default:
+ return "", trace.BadParameter("unknown integration CA type %q", req.AuthType)
+ }
+}
+
+func fetchIntegrationCAKeySet(ctx context.Context, client authclient.ClientI, integration string) (*types.CAKeySet, error) {
+ resp, err := client.IntegrationsClient().ExportIntegrationCertAuthorities(ctx, &integrationpb.ExportIntegrationCertAuthoritiesRequest{
+ Integration: integration,
+ })
+ if err != nil {
+ return nil, trace.Wrap(err)
+ }
+ return resp.CertAuthorities, nil
+}
+
+func exportGitHubCAs(keySet *types.CAKeySet, req ExportAuthoritiesRequest) (string, error) {
+ ret := strings.Builder{}
+ for _, key := range keySet.SSH {
+ if req.ExportAuthorityFingerprint != "" {
+ if fingerprint, err := sshutils.AuthorizedKeyFingerprint(key.PublicKey); err != nil {
+ return "", trace.Wrap(err)
+ } else if !sshutils.EqualFingerprints(req.ExportAuthorityFingerprint, fingerprint) {
+ continue
+ }
+ }
+
+ // GitHub only needs the keys like "ssh-rsa xxx" so print them without
+ // cert-authority for easier copy-and-paste.
+ ret.WriteString(fmt.Sprintf("%s integration=%s\n", strings.TrimSpace(string(key.PublicKey)), req.Integration))
+ }
+ return ret.String(), nil
+}
diff --git a/lib/client/ca_export_test.go b/lib/client/ca_export_test.go
index 32082427ee523..cf7ff693716fe 100644
--- a/lib/client/ca_export_test.go
+++ b/lib/client/ca_export_test.go
@@ -26,18 +26,22 @@ import (
"testing"
"github.com/stretchr/testify/require"
+ "google.golang.org/grpc"
"github.com/gravitational/teleport/api/client/proto"
+ integrationpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1"
"github.com/gravitational/teleport/api/mfa"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/auth/authclient"
+ "github.com/gravitational/teleport/lib/fixtures"
)
type mockAuthClient struct {
authclient.ClientI
- server *auth.Server
+ server *auth.Server
+ integrationsClient mockIntegrationsClient
}
func (m *mockAuthClient) GetDomainName(ctx context.Context) (string, error) {
@@ -57,6 +61,21 @@ func (m *mockAuthClient) PerformMFACeremony(ctx context.Context, challengeReques
return nil, &mfa.ErrMFANotRequired
}
+func (m *mockAuthClient) IntegrationsClient() integrationpb.IntegrationServiceClient {
+ return &m.integrationsClient
+}
+
+type mockIntegrationsClient struct {
+ integrationpb.IntegrationServiceClient
+ caKeySet *types.CAKeySet
+}
+
+func (m *mockIntegrationsClient) ExportIntegrationCertAuthorities(ctx context.Context, in *integrationpb.ExportIntegrationCertAuthoritiesRequest, opts ...grpc.CallOption) (*integrationpb.ExportIntegrationCertAuthoritiesResponse, error) {
+ return &integrationpb.ExportIntegrationCertAuthoritiesResponse{
+ CertAuthorities: m.caKeySet,
+ }, nil
+}
+
func TestExportAuthorities(t *testing.T) {
ctx := context.Background()
const localClusterName = "localcluster"
@@ -100,6 +119,10 @@ func TestExportAuthorities(t *testing.T) {
require.NotNil(t, privKey, "x509.ParsePKCS8PrivateKey returned a nil key")
}
+ validateGitHubCAFunc := func(t *testing.T, s string) {
+ require.Contains(t, s, fixtures.SSHCAPublicKey)
+ }
+
for _, exportSecrets := range []bool{false, true} {
for _, tt := range []struct {
name string
@@ -247,10 +270,33 @@ func TestExportAuthorities(t *testing.T) {
assertNoSecrets: validateTLSCertificateDERFunc,
assertSecrets: validateRSAPrivateKeyDERFunc,
},
+ {
+ name: "github missing integration",
+ req: ExportAuthoritiesRequest{
+ AuthType: "github",
+ },
+ errorCheck: require.Error,
+ },
+ {
+ name: "github",
+ req: ExportAuthoritiesRequest{
+ AuthType: "github",
+ Integration: "my-github",
+ },
+ errorCheck: require.NoError,
+ assertNoSecrets: validateGitHubCAFunc,
+ },
} {
t.Run(fmt.Sprintf("%s_exportSecrets_%v", tt.name, exportSecrets), func(t *testing.T) {
mockedClient := &mockAuthClient{
server: testAuth.AuthServer,
+ integrationsClient: mockIntegrationsClient{
+ caKeySet: &types.CAKeySet{
+ SSH: []*types.SSHKeyPair{{
+ PublicKey: []byte(fixtures.SSHCAPublicKey),
+ }},
+ },
+ },
}
var (
err error
@@ -264,6 +310,10 @@ func TestExportAuthorities(t *testing.T) {
checkFunc = tt.assertSecrets
}
+ if checkFunc == nil {
+ t.Skip("assert func not provided")
+ }
+
exported, err = exportFunc(ctx, mockedClient, tt.req)
tt.errorCheck(t, err)
diff --git a/lib/services/plugin_static_credentials.go b/lib/services/plugin_static_credentials.go
index cdb32551ef857..7759df5f933c6 100644
--- a/lib/services/plugin_static_credentials.go
+++ b/lib/services/plugin_static_credentials.go
@@ -44,6 +44,9 @@ type PluginStaticCredentials interface {
// DeletePluginStaticCredentials will delete a plugin static credentials resource.
DeletePluginStaticCredentials(ctx context.Context, name string) error
+
+ // GetAllPluginStaticCredentials will get all plugin static credentials.
+ GetAllPluginStaticCredentials(ctx context.Context) ([]types.PluginStaticCredentials, error)
}
// MarshalPluginStaticCredentials marshals PluginStaticCredentials resource to JSON.
diff --git a/lib/sshutils/fingerprint.go b/lib/sshutils/fingerprint.go
index bae36ae07857e..010dc6bb7b783 100644
--- a/lib/sshutils/fingerprint.go
+++ b/lib/sshutils/fingerprint.go
@@ -19,6 +19,8 @@
package sshutils
import (
+ "strings"
+
"github.com/gravitational/trace"
"golang.org/x/crypto/ssh"
)
@@ -47,3 +49,18 @@ func PrivateKeyFingerprint(keyBytes []byte) (string, error) {
}
return Fingerprint(signer.PublicKey()), nil
}
+
+// fingerprintPrefix is the fingerprint prefix added by ssh.FingerprintSHA256.
+const fingerprintPrefix = "SHA256:"
+
+func maybeAddPrefix(fingerprint string) string {
+ if !strings.HasPrefix(fingerprint, fingerprintPrefix) {
+ return fingerprintPrefix + fingerprint
+ }
+ return fingerprint
+}
+
+// EqualFingerprints checks if two finger prints are equal.
+func EqualFingerprints(a, b string) bool {
+ return strings.EqualFold(maybeAddPrefix(a), maybeAddPrefix(b))
+}
diff --git a/lib/sshutils/fingerprint_test.go b/lib/sshutils/fingerprint_test.go
new file mode 100644
index 0000000000000..a17878236008a
--- /dev/null
+++ b/lib/sshutils/fingerprint_test.go
@@ -0,0 +1,64 @@
+/*
+ * 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 sshutils
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestEqualFingerprints(t *testing.T) {
+ tests := []struct {
+ name string
+ a string
+ b string
+ check require.BoolAssertionFunc
+ }{
+ {
+ name: "equal",
+ a: "SHA256:fingerprint",
+ b: "SHA256:fingerprint",
+ check: require.True,
+ },
+ {
+ name: "not equal",
+ a: "SHA256:fingerprint",
+ b: "SHA256:fingerprint2",
+ check: require.False,
+ },
+ {
+ name: "equal without prefix",
+ a: "SHA256:fingerprint",
+ b: "fingerprint",
+ check: require.True,
+ },
+ {
+ name: "equal fold",
+ a: "FINGERPRINT",
+ b: "SHA256:fingerprint",
+ check: require.True,
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ test.check(t, EqualFingerprints(test.a, test.b))
+ })
+ }
+}
diff --git a/tool/tctl/common/auth_command.go b/tool/tctl/common/auth_command.go
index 141a38e8fe46d..f46934965c9b6 100644
--- a/tool/tctl/common/auth_command.go
+++ b/tool/tctl/common/auth_command.go
@@ -87,6 +87,7 @@ type AuthCommand struct {
caType string
streamTarfile bool
identityWriter identityfile.ConfigWriter
+ integration string
rotateGracePeriod time.Duration
rotateType string
@@ -116,6 +117,7 @@ func (a *AuthCommand) Initialize(app *kingpin.Application, config *servicecfg.Co
a.authExport.Flag("type",
fmt.Sprintf("export certificate type (%v)", strings.Join(allowedCertificateTypes, ", "))).
EnumVar(&a.authType, allowedCertificateTypes...)
+ a.authExport.Flag("integration", "Name of the integration. Only applies to \"github\" CAs.").StringVar(&a.integration)
a.authGenerate = auth.Command("gen", "Generate a new SSH keypair.").Hidden()
a.authGenerate.Flag("pub-key", "path to the public key").Required().StringVar(&a.genPubPath)
@@ -206,6 +208,7 @@ var allowedCertificateTypes = []string{
"db-client-der",
"openssh",
"saml-idp",
+ "github",
}
// allowedCRLCertificateTypes list of certificate authorities types that can
@@ -233,6 +236,7 @@ func (a *AuthCommand) ExportAuthorities(ctx context.Context, clt *authclient.Cli
AuthType: a.authType,
ExportAuthorityFingerprint: a.exportAuthorityFingerprint,
UseCompatVersion: a.compatVersion == "1.0",
+ Integration: a.integration,
},
)
if err != nil {