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

Adding OIDC auth functionality to the Azure integration #51219

Draft
wants to merge 5 commits into
base: mvbrock/azure-integration-disco-azure-srv
Choose a base branch
from
Draft
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
11 changes: 11 additions & 0 deletions api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4829,6 +4829,17 @@ func (c *Client) GenerateAWSOIDCToken(ctx context.Context, integration string) (
return resp.GetToken(), nil
}

func (c *Client) GenerateAzureOIDCToken(ctx context.Context, integration string) (string, error) {
resp, err := c.integrationsClient().GenerateAzureOIDCToken(ctx, &integrationpb.GenerateAzureOIDCTokenRequest{
Integration: integration,
})
if err != nil {
return "", trace.Wrap(err)
}

return resp.GetToken(), nil
}

// PluginsClient returns an unadorned Plugins client, using the underlying
// Auth gRPC connection.
// Clients connecting to non-Enterprise clusters, or older Teleport versions,
Expand Down
395 changes: 255 additions & 140 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.

17 changes: 17 additions & 0 deletions api/proto/teleport/integration/v1/integration_service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ service IntegrationService {
// GenerateAWSOIDCToken generates a token to be used when executing an AWS OIDC Integration action.
rpc GenerateAWSOIDCToken(GenerateAWSOIDCTokenRequest) returns (GenerateAWSOIDCTokenResponse);

// GenerateAzureOIDCToken generates a token to be used when executing an Azure OIDC Integration action.
rpc GenerateAzureOIDCToken(GenerateAzureOIDCTokenRequest) returns (GenerateAzureOIDCTokenResponse);

// GenerateGitHubUserCert signs a SSH certificate for GitHub integration.
rpc GenerateGitHubUserCert(GenerateGitHubUserCertRequest) returns (GenerateGitHubUserCertResponse);

Expand Down Expand Up @@ -119,6 +122,20 @@ message GenerateAWSOIDCTokenResponse {
string token = 1;
}

// GenerateAzureOIDCTokenRequest are the parameters used to request an AWS OIDC
// Integration token.
message GenerateAzureOIDCTokenRequest {
// Integration is the Azure OIDC Integration name.
// Required.
string integration = 2;
}

// GenerateAzureOIDCTokenResponse contains a signed Azure OIDC Integration token.
message GenerateAzureOIDCTokenResponse {
// 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 {
Expand Down
5 changes: 5 additions & 0 deletions lib/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,11 @@ func (r *Services) GenerateAWSOIDCToken(ctx context.Context, integration string)
return r.IntegrationsTokenGenerator.GenerateAWSOIDCToken(ctx, integration)
}

// GenerateAzureOIDCToken generates a token to be used to execute an AWS OIDC Integration action.
func (r *Services) GenerateAzureOIDCToken(ctx context.Context, integration string) (string, error) {
return r.IntegrationsTokenGenerator.GenerateAzureOIDCToken(ctx, integration)
}

var (
generateRequestsCount = prometheus.NewCounter(
prometheus.CounterOpts{
Expand Down
7 changes: 7 additions & 0 deletions lib/auth/authclient/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,9 @@ type DiscoveryAccessPoint interface {
// GenerateAWSOIDCToken generates a token to be used to execute an AWS OIDC Integration action.
GenerateAWSOIDCToken(ctx context.Context, integration string) (string, error)

// GenerateAzureOIDCToken generates a token to be used to execute an Azure OIDC Integration action.
GenerateAzureOIDCToken(ctx context.Context, integration string) (string, error)

// EnrollEKSClusters enrolls EKS clusters into Teleport by installing teleport-kube-agent chart on the clusters.
EnrollEKSClusters(context.Context, *integrationpb.EnrollEKSClustersRequest, ...grpc.CallOption) (*integrationpb.EnrollEKSClustersResponse, error)

Expand Down Expand Up @@ -1484,6 +1487,10 @@ func (w *DiscoveryWrapper) GenerateAWSOIDCToken(ctx context.Context, integration
return w.NoCache.GenerateAWSOIDCToken(ctx, integration)
}

func (w *DiscoveryWrapper) GenerateAzureOIDCToken(ctx context.Context, integration string) (string, error) {
return w.NoCache.GenerateAzureOIDCToken(ctx, integration)
}

// EnrollEKSClusters enrolls EKS clusters into Teleport by installing teleport-kube-agent chart on the clusters.
func (w *DiscoveryWrapper) EnrollEKSClusters(ctx context.Context, req *integrationpb.EnrollEKSClustersRequest, _ ...grpc.CallOption) (*integrationpb.EnrollEKSClustersResponse, error) {
return w.NoCache.EnrollEKSClusters(ctx, req)
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 @@ -1703,6 +1703,9 @@ type ClientI interface {
// GenerateAWSOIDCToken generates a token to be used to execute an AWS OIDC Integration action.
GenerateAWSOIDCToken(ctx context.Context, integration string) (string, error)

// GenerateAzureOIDCToken generates a token to be used to execute an Azure OIDC Integration action.
GenerateAzureOIDCToken(ctx context.Context, integration string) (string, error)

// ResetAuthPreference resets cluster auth preference to defaults.
ResetAuthPreference(ctx context.Context) error

Expand Down
20 changes: 20 additions & 0 deletions lib/auth/integration/integrationv1/azureoidc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package integrationv1

import (
"context"
integrationpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1"
"github.com/gravitational/teleport/lib/integrations/azureoidc"
"github.com/gravitational/trace"
)

func (s *Service) GenerateAzureOIDCToken(ctx context.Context, req *integrationpb.GenerateAzureOIDCTokenRequest) (*integrationpb.GenerateAzureOIDCTokenResponse, error) {
_, err := s.cache.GetIntegration(ctx, req.Integration)
if err != nil {
return nil, trace.Wrap(err)
}
token, err := azureoidc.GenerateEntraOIDCToken(ctx, s.cache, s.keyStoreManager, s.clock)
if err != nil {
return nil, trace.Wrap(err)
}
return &integrationpb.GenerateAzureOIDCTokenResponse{Token: token}, nil
}
2 changes: 2 additions & 0 deletions lib/services/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ type IntegrationsGetter interface {
type IntegrationsTokenGenerator interface {
// GenerateAWSOIDCToken generates a token to be used to execute an AWS OIDC Integration action.
GenerateAWSOIDCToken(ctx context.Context, integration string) (string, error)
// GenerateAzureOIDCToken generates a token to be used to execute an Azure OIDC Integration action.
GenerateAzureOIDCToken(ctx context.Context, integration string) (string, error)
}

// MarshalIntegration marshals the Integration resource to JSON.
Expand Down
22 changes: 19 additions & 3 deletions lib/srv/discovery/access_graph_azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ func (s *Server) initializeAndWatchAzureAccessGraph(ctx context.Context, reloadC

// initTAGAzureWatchers initializes the TAG Azure watchers
func (s *Server) initTAGAzureWatchers(ctx context.Context, cfg *Config) error {
staticFetchers, err := s.accessGraphAzureFetchersFromMatchers(cfg.Matchers, "" /* discoveryConfigName */)
staticFetchers, err := s.accessGraphAzureFetchersFromMatchers(ctx, cfg.Matchers, "" /* discoveryConfigName */)
if err != nil {
s.Log.ErrorContext(ctx, "Error initializing access graph fetchers", "error", err)
}
Expand Down Expand Up @@ -386,17 +386,33 @@ func (s *Server) initTAGAzureWatchers(ctx context.Context, cfg *Config) error {

// accessGraphAzureFetchersFromMatchers converts matcher configuration to fetchers for Azure resource synchronization
func (s *Server) accessGraphAzureFetchersFromMatchers(
matchers Matchers, discoveryConfigName string) ([]*azuresync.Fetcher, error) {
ctx context.Context, matchers Matchers, discoveryConfigName string,
) ([]*azuresync.Fetcher, error) {
var fetchers []*azuresync.Fetcher
var errs []error
if matchers.AccessGraph == nil {
return fetchers, nil
}
for _, matcher := range matchers.AccessGraph.Azure {
// Fetch the integration configured for this fetcher
integration, err := s.AccessPoint.GetIntegration(ctx, matcher.Integration)
if err != nil {
errs = append(errs, err)
continue
}
azureIntegration := integration.GetAzureOIDCIntegrationSpec()
if azureIntegration == nil {
errs = append(errs, trace.Errorf("integration %s has no Azure OIDC integration spec", matcher.Integration))
continue
}
fetcherCfg := azuresync.Config{
TokenGeneratorFn: func(integration string) (string, error) {
return s.AccessPoint.GenerateAzureOIDCToken(ctx, integration)
},
CloudClients: s.CloudClients,
SubscriptionID: matcher.SubscriptionID,
Integration: matcher.Integration,
IntegrationName: matcher.Integration,
Integration: azureIntegration,
DiscoveryConfigName: discoveryConfigName,
}
fetcher, err := azuresync.NewFetcher(fetcherCfg, s.ctx)
Expand Down
2 changes: 1 addition & 1 deletion lib/srv/discovery/discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -1831,7 +1831,7 @@ func (s *Server) upsertDynamicMatchers(ctx context.Context, dc *discoveryconfig.
s.dynamicTAGAWSFetchers[dc.GetName()] = awsSyncMatchers
s.muDynamicTAGAWSFetchers.Unlock()

azureSyncMatchers, err := s.accessGraphAzureFetchersFromMatchers(matchers, dc.GetName())
azureSyncMatchers, err := s.accessGraphAzureFetchersFromMatchers(ctx, matchers, dc.GetName())
if err != nil {
return trace.Wrap(err)
}
Expand Down
33 changes: 28 additions & 5 deletions lib/srv/discovery/fetchers/azure-sync/azure-sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package azuresync

import (
"context"
"github.com/gravitational/teleport/api/types"

"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/gravitational/trace"
Expand Down Expand Up @@ -47,8 +48,10 @@ const FetcherConcurrency = 4
type Config struct {
CloudClients cloud.Clients
SubscriptionID string
Integration string
IntegrationName string
Integration *types.AzureOIDCIntegrationSpecV1
DiscoveryConfigName string
TokenGeneratorFn func(integration string) (string, error)
}

// Resources represents the set of resources fetched from Azure
Expand All @@ -74,10 +77,30 @@ type Fetcher struct {

// NewFetcher returns a new fetcher based on configuration parameters
func NewFetcher(cfg Config, ctx context.Context) (*Fetcher, error) {
// Establish the credential from the managed identity
cred, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
return nil, trace.Wrap(err)
var cred msgraph.AzureTokenProvider
var err error
if cfg.Integration == nil {
// Establish the credential from the managed identity
cred, err = azidentity.NewDefaultAzureCredential(nil)
if err != nil {
return nil, trace.Wrap(err)
}
} else {
// Establish the credential from the Azure OIDC integration
if cfg.TokenGeneratorFn == nil {
return nil, trace.BadParameter("missing token generator function")
}
assertionFn := func(ctx context.Context) (string, error) {
token, err := cfg.TokenGeneratorFn(cfg.IntegrationName)
if err != nil {
return "", trace.Wrap(err)
}
return token, nil
}
cred, err = azidentity.NewClientAssertionCredential(cfg.Integration.TenantID, cfg.Integration.ClientID, assertionFn, nil)
if err != nil {
return nil, trace.Wrap(err, "failed to create Azure client assertion credential")
}
}

// Create the clients for the fetcher
Expand Down
Loading