Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
36429b9
Protobuf and configuration for Access Graph Azure Discovery
mvbrock Dec 17, 2024
59c9049
Fixing rebase after protobuf gen
mvbrock Dec 18, 2024
f6ddddf
Updating to use existing msgraph client
mvbrock Dec 19, 2024
d2f2ba7
PR feedback
mvbrock Dec 20, 2024
6c68433
Using variadic options
mvbrock Jan 6, 2025
9788436
Removing memberOf expansion
mvbrock Jan 6, 2025
3a49525
Expanding memberships by calling memberOf on each user
mvbrock Jan 7, 2025
0322a83
PR feedback
mvbrock Jan 9, 2025
de61214
Rebase go.sum stuff
mvbrock Jan 9, 2025
cb6b8f5
Go mod tidy
mvbrock Jan 9, 2025
c9aff9f
Fixing go.mod
mvbrock Jan 9, 2025
f2f9634
Update lib/msgraph/paginated.go
mvbrock Jan 10, 2025
856e66d
PR feedback
mvbrock Jan 10, 2025
d1569e3
Protobuf and configuration for Access Graph Azure Discovery
mvbrock Dec 17, 2024
d012d54
Adding Azure sync functionality which can be called by the Azure fetcher
mvbrock Dec 17, 2024
c40a317
Protobuf update
mvbrock Dec 18, 2024
9c4431a
Linting
mvbrock Jan 13, 2025
0a7bf93
PR feedback
mvbrock Jan 15, 2025
4a32974
PR feedback
mvbrock Jan 16, 2025
df95f49
Updating to use existing msgraph client
mvbrock Dec 19, 2024
34367b8
PR feedback
mvbrock Dec 20, 2024
888fb06
Using variadic options
mvbrock Jan 6, 2025
76e5f4f
Removing memberOf expansion
mvbrock Jan 6, 2025
1651a36
Expanding memberships by calling memberOf on each user
mvbrock Jan 7, 2025
65e7687
PR feedback
mvbrock Jan 9, 2025
2697287
Rebase go.sum stuff
mvbrock Jan 9, 2025
c238a39
PR feedback
mvbrock Jan 10, 2025
faec959
Protobuf and configuration for Access Graph Azure Discovery
mvbrock Dec 17, 2024
a166e79
Protobuf gen fix
mvbrock Dec 18, 2024
a6f03f7
Rebase fixes
mvbrock Jan 22, 2025
7aeab0f
More cleanup
mvbrock Jan 22, 2025
1e35ed2
e ref update
mvbrock Jan 10, 2025
e2f6e24
Invoking token generation and returning the response
mvbrock Jan 18, 2025
dc9c000
Quick test with a message to make sure RPC is invoked
mvbrock Jan 18, 2025
738cee6
Skeleton of new Azure OIDC RPC call
mvbrock Jan 18, 2025
71ce322
Fetching the Azure OIDC token during fetcher creation and establishin…
mvbrock Jan 19, 2025
5a60068
PR feedback; restricting token requests to auth, discovery, and proxy…
mvbrock Jan 21, 2025
dc04d94
Lint
mvbrock Jan 22, 2025
7242fba
Fixing mocks
mvbrock Jan 22, 2025
384ab8f
Fix imports
mvbrock Jan 22, 2025
8093659
Fix test
mvbrock Jan 22, 2025
770cafc
Rebase fxes
mvbrock Jan 23, 2025
ebcdab8
Adding back OIDC fetching, accidentally removed it during rebase
mvbrock Jan 23, 2025
da36e40
e ref
mvbrock Jan 23, 2025
31064b0
Lint
mvbrock Jan 23, 2025
696d4aa
Fix imports
mvbrock Jan 23, 2025
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
12 changes: 12 additions & 0 deletions api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4835,6 +4835,18 @@ func (c *Client) GenerateAWSOIDCToken(ctx context.Context, integration string) (
return resp.GetToken(), nil
}

// GenerateAzureOIDCToken generates a token to be used when executing an Azure OIDC Integration action.
func (c *Client) GenerateAzureOIDCToken(ctx context.Context, integration string) (string, error) {
Comment thread
mvbrock marked this conversation as resolved.
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 Azure OIDC
// Integration token.
message GenerateAzureOIDCTokenRequest {
// Integration is the Azure OIDC Integration name.
// Required.
string integration = 1;
}

// 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 Azure 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
8 changes: 8 additions & 0 deletions lib/auth/authclient/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,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 @@ -1437,6 +1440,11 @@ func (w *DiscoveryWrapper) GenerateAWSOIDCToken(ctx context.Context, integration
return w.NoCache.GenerateAWSOIDCToken(ctx, integration)
}

// GenerateAzureOIDCToken generates a token to be used to execute an Azure OIDC Integration action.
func (w *DiscoveryWrapper) GenerateAzureOIDCToken(ctx context.Context, integration string) (string, error) {
Comment thread
mvbrock marked this conversation as resolved.
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
52 changes: 52 additions & 0 deletions lib/auth/integration/integrationv1/azureoidc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Teleport
* Copyright (C) 2025 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 integrationv1

import (
"context"

"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/authz"
"github.com/gravitational/teleport/lib/integrations/azureoidc"
)

// GenerateAzureOIDCToken generates a token to be used to execute an Azure OIDC Integration action.
func (s *Service) GenerateAzureOIDCToken(ctx context.Context, req *integrationpb.GenerateAzureOIDCTokenRequest) (*integrationpb.GenerateAzureOIDCTokenResponse, error) {
Comment thread
mvbrock marked this conversation as resolved.
authCtx, err := s.authorizer.Authorize(ctx)
if err != nil {
return nil, trace.Wrap(err)
}
_, err = s.cache.GetIntegration(ctx, req.Integration)
if err != nil {
return nil, trace.Wrap(err)
}
for _, allowedRole := range []types.SystemRole{types.RoleDiscovery, types.RoleAuth, types.RoleProxy} {
if authz.HasBuiltinRole(*authCtx, string(allowedRole)) {
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
}
}
return nil, trace.AccessDenied("token generation is only available to auth, proxy or discovery services")
}
113 changes: 113 additions & 0 deletions lib/auth/integration/integrationv1/azureoidc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Teleport
* Copyright (C) 2025 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 integrationv1

import (
"testing"

"github.com/gravitational/trace"
"github.com/stretchr/testify/require"

integrationv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/authz"
"github.com/gravitational/teleport/lib/jwt"
"github.com/gravitational/teleport/lib/tlsca"
)

func TestGenerateAzureOIDCToken(t *testing.T) {
t.Parallel()
clusterName := "test-cluster"
integrationName := "my-integration"

publicURL := "https://example.com"

ca := newCertAuthority(t, types.HostCA, clusterName)
ctx, localClient, resourceSvc := initSvc(t, ca, clusterName, publicURL)

// Create integration
ig, err := types.NewIntegrationAzureOIDC(
types.Metadata{Name: integrationName},
&types.AzureOIDCIntegrationSpecV1{
TenantID: "foo",
ClientID: "bar",
},
)
require.NoError(t, err)
_, err = localClient.CreateIntegration(ctx, ig)
require.NoError(t, err)

t.Run("only Auth, Discovery, and Proxy roles should be able to generate Azure tokens", func(t *testing.T) {
// A dummy user should not be able to generate Azure OIDC tokens
ctx = authorizerForDummyUser(t, ctx, types.RoleSpecV6{
Allow: types.RoleConditions{Rules: []types.Rule{
{Resources: []string{types.KindIntegration}, Verbs: []string{types.VerbUse}},
}},
}, localClient)
_, err = resourceSvc.GenerateAzureOIDCToken(ctx, &integrationv1.GenerateAzureOIDCTokenRequest{Integration: integrationName})
require.True(t, trace.IsAccessDenied(err), "expected AccessDenied error, got %T", err)

// Auth, Discovery, and Proxy roles should be able to generate Azure OIDC tokens
for _, allowedRole := range []types.SystemRole{types.RoleAuth, types.RoleDiscovery, types.RoleProxy} {
ctx = authz.ContextWithUser(ctx, authz.BuiltinRole{
Role: types.RoleInstance,
AdditionalSystemRoles: []types.SystemRole{allowedRole},
Username: string(allowedRole),
Identity: tlsca.Identity{
Username: string(allowedRole),
},
})

_, err := resourceSvc.GenerateAzureOIDCToken(ctx, &integrationv1.GenerateAzureOIDCTokenRequest{Integration: integrationName})
require.NoError(t, err)
}
})

t.Run("validate the Azure token", func(t *testing.T) {
ctx = authz.ContextWithUser(ctx, authz.BuiltinRole{
Role: types.RoleInstance,
AdditionalSystemRoles: []types.SystemRole{types.RoleDiscovery},
Username: string(types.RoleDiscovery),
Identity: tlsca.Identity{
Username: string(types.RoleDiscovery),
},
})
resp, err := resourceSvc.GenerateAzureOIDCToken(ctx, &integrationv1.GenerateAzureOIDCTokenRequest{
Integration: integrationName,
})
require.NoError(t, err)

// Validate JWT against public key
require.NotEmpty(t, ca.GetActiveKeys().JWT)
jwtPubKey := ca.GetActiveKeys().JWT[0].PublicKey
publicKey, err := keys.ParsePublicKey(jwtPubKey)
require.NoError(t, err)
key, err := jwt.New(&jwt.Config{
ClusterName: clusterName,
Clock: resourceSvc.clock,
PublicKey: publicKey,
})
require.NoError(t, err)

// Verify the Azure token using the JWT
_, err = key.VerifyAzureToken(resp.Token)
require.NoError(t, err)
})
}
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
1 change: 1 addition & 0 deletions lib/srv/discovery/access_graph_azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ func (s *Server) accessGraphAzureFetchersFromMatchers(
SubscriptionID: matcher.SubscriptionID,
Integration: matcher.Integration,
DiscoveryConfigName: discoveryConfigName,
OIDCCredentials: s.AccessPoint,
}
fetcher, err := azuresync.NewFetcher(fetcherCfg, s.ctx)
if err != nil {
Expand Down
Loading