-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding OIDC auth functionality to the Azure integration (#51219)
* Protobuf and configuration for Access Graph Azure Discovery * Fixing rebase after protobuf gen * Updating to use existing msgraph client * PR feedback * Using variadic options * Removing memberOf expansion * Expanding memberships by calling memberOf on each user * PR feedback * Rebase go.sum stuff * Go mod tidy * Fixing go.mod * Update lib/msgraph/paginated.go Co-authored-by: Tiago Silva <tiago.silva@goteleport.com> * PR feedback * Protobuf and configuration for Access Graph Azure Discovery * Adding Azure sync functionality which can be called by the Azure fetcher * Protobuf update * Linting * PR feedback * PR feedback * Updating to use existing msgraph client * PR feedback * Using variadic options * Removing memberOf expansion * Expanding memberships by calling memberOf on each user * PR feedback * Rebase go.sum stuff * PR feedback * Protobuf and configuration for Access Graph Azure Discovery * Protobuf gen fix * Rebase fixes * More cleanup * e ref update * Invoking token generation and returning the response * Quick test with a message to make sure RPC is invoked * Skeleton of new Azure OIDC RPC call * Fetching the Azure OIDC token during fetcher creation and establishing a credential assertion approach * PR feedback; restricting token requests to auth, discovery, and proxy roles. * Lint * Fixing mocks * Fix imports * Fix test * Rebase fxes * Adding back OIDC fetching, accidentally removed it during rebase * e ref * Lint * Fix imports --------- Co-authored-by: Tiago Silva <tiago.silva@goteleport.com>
- Loading branch information
Showing
13 changed files
with
542 additions
and
144 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
395 changes: 255 additions & 140 deletions
395
api/gen/proto/go/teleport/integration/v1/integration_service.pb.go
Large diffs are not rendered by default.
Oops, something went wrong.
40 changes: 40 additions & 0 deletions
40
api/gen/proto/go/teleport/integration/v1/integration_service_grpc.pb.go
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { | ||
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") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.