Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GitHub Proxy part 3: gen github user cert and export CA #49396

Merged
merged 7 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
445 changes: 367 additions & 78 deletions api/gen/proto/go/teleport/integration/v1/integration_service.pb.go

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions api/proto/teleport/integration/v1/integration_service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}
3 changes: 3 additions & 0 deletions lib/auth/authclient/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
3 changes: 3 additions & 0 deletions lib/auth/authclient/clt.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
107 changes: 107 additions & 0 deletions lib/auth/integration/credentials/credentials.go
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

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))
}
}
Loading
Loading