Skip to content

Entra ID integration: add onboarding script#41811

Merged
justinas merged 19 commits intomasterfrom
justinas/entra-id-onboarding-script
May 27, 2024
Merged

Entra ID integration: add onboarding script#41811
justinas merged 19 commits intomasterfrom
justinas/entra-id-onboarding-script

Conversation

@justinas
Copy link
Copy Markdown
Contributor

@justinas justinas commented May 21, 2024

Adds a new teleport integration configure subcommand to set up Azure OIDC integration. The command sets up:

  • An Entra ID enterprise application, both as an SSO connector to allow login with Entra to Teleport, and as a way for us to access Entra ID directory programatically (via OIDC auth).
  • Optionally, when access graph support is requested, queries information about existing enterprise applications from the "private" Azure API (see RFD for more info) and produces a JSON payload that the user is expected to upload when finishing the integration onboarding in the UI.

Also adds the web endpoint in the Proxy that generates the script wrapper for this.

Still missing:

  • Frontend to onboard Entra that utilizes this. Will come as a subsequent PR.

@justinas justinas force-pushed the justinas/entra-id-onboarding-script branch from c718ff9 to eb53161 Compare May 21, 2024 13:14
@justinas justinas force-pushed the justinas/entra-id-onboarding-script branch from eb53161 to d4fb0c1 Compare May 21, 2024 13:20
@justinas justinas changed the title Justinas/entra id onboarding script Entra ID integration: add onboarding script May 21, 2024
@justinas justinas marked this pull request as ready for review May 21, 2024 16:57
@justinas justinas requested review from jakule and tigrato May 21, 2024 16:58
@github-actions
Copy link
Copy Markdown
Contributor

The PR changelog entry failed validation: Changelog entry not found in the PR body. Please add a "no-changelog" label to the PR, or changelog lines starting with changelog: followed by the changelog entries for the PR.

@github-actions github-actions Bot requested review from GavinFrazar and ryanclark May 21, 2024 16:58
@justinas justinas added no-changelog Indicates that a PR does not require a changelog entry and removed size/lg labels May 21, 2024
@GavinFrazar GavinFrazar removed their request for review May 22, 2024 01:17
// Get information about enterprise apps
appResp, err := graphClient.Applications().Get(ctx, nil)
if err != nil {
panic(err)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think panicking here is a good idea.

_, err := io.Copy(writer, bytes.NewReader(src))
// We do not expect in-memory bytes I/O to fail.
if err != nil {
panic(err)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd still return the error here. The chances are close to 0, but we should not use panics in non critical code (unrecoverable failure that should stop the whole app).

return appID, tenantID, trace.Wrap(err)
}

displayName := "Teleport" + " " + strings.TrimPrefix(proxyPublicAddr, "https://")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: You can use url.Url to extract the hostname and make sure that the output is correct.

}

spList := spResponse.GetValue()
if len(spList) < 1 {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To make sure only one is returned?

Suggested change
if len(spList) < 1 {
if len(spList) != 1 {

Comment thread lib/integrations/azureoidc/private.go Outdated
slog.DebugContext(ctx, "trying token", "client_id", token.ClientID)
token, err := exchangeToken(ctx, tenantID, token)
if err != nil {
slog.ErrorContext(ctx, "error exchanging token", "err", err)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If incorrect token doesn't break the processing as another one can work this is not an error. I'd lower down the level to warning or even debug. If not token is found, an error is returned from this function anyway.

Comment thread lib/web/integrations_azureoidc.go
fmt.Println("Success! Use the following information to finish the integration onboarding in Teleport:")
fmt.Printf("Tenant ID: %s\nClient ID: %s\n", tenantID, appID)
if params.AccessGraph {
fmt.Println("Because Access Graph integration was enabled, we also created a file with the necessary data.")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that the fact that access graph is enabled is important. The important part is that something else needs to be done to finish the setup.

Suggested change
fmt.Println("Because Access Graph integration was enabled, we also created a file with the necessary data.")
fmt.Println("To finish the setup you will need `cache.json` file that we created for you.")

@justinas justinas requested a review from jakule May 24, 2024 15:58
Copy link
Copy Markdown
Contributor

@jakule jakule left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved with a few blocks to not block.

Comment thread lib/config/configuration.go Outdated
// AccessGraph is a flag indicating that access graph integration is requested.
// When this is true, the integration script will produce
// a cache file necessary for TAG synchronization.
AccessGraph bool
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I'd rename it to AccessGraphEnabled or something like that, but I also don't mind the current version.

}
sp, err := graphClient.ServicePrincipalsWithAppId(appID).Get(ctx, nil)
if err != nil {
slog.ErrorContext(ctx, "could not retrieve service principal", "app_id", *appID, "error", err)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a bug. If the error is not nil we should not try to call GetId() a few lines below.

spID := sp.GetId()
if spID == nil {
slog.WarnContext(ctx, "service principal ID is nil", "app_id", *appID)
continue
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of putting continue everywhere you can extract this logic to a separate function, return errors as expected and log them here in one place.


federatedSSOV2Compressed, err := gzipBytes(federatedSSOV2)
if err != nil {
slog.WarnContext(ctx, "can not compress the FederatedSsoV2 payload", "error", err)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same as above. Is it fine to use federatedSSOV2Compressed if an error is returned?

Comment thread lib/integrations/azureoidc/private.go Outdated
return "", trace.Wrap(err)
}

resp, err := (&http.Client{}).Do(req)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use defaults.HTTPClient() to use client with some "sane" defaults.

Comment thread lib/integrations/azureoidc/private.go Outdated
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", accessToken))
req.Header.Add("x-ms-client-request-id", uuid.NewString())

resp, err := (&http.Client{}).Do(req)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use defaults.HTTPClient() to use client with custom config

if len(profile.Subscriptions) == 0 {
return "", trace.BadParameter("subscription not found")
}
return profile.Subscriptions[0].TenantID, nil
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please comment why other subscriptions are ignored.

Comment thread lib/web/integrations_azureoidc.go
@jakule
Copy link
Copy Markdown
Contributor

jakule commented May 24, 2024

@tigrato Can you please take a look?

@public-teleport-github-review-bot public-teleport-github-review-bot Bot removed the request for review from ryanclark May 25, 2024 12:58
@justinas justinas added this pull request to the merge queue May 27, 2024
Merged via the queue into master with commit ec8f212 May 27, 2024
@justinas justinas deleted the justinas/entra-id-onboarding-script branch May 27, 2024 19:29
justinas added a commit that referenced this pull request Jun 6, 2024
* Add Entra ID integration onboarding script

* Adapt after proto update

* Validate names in azure script handler, add test

* Add license headers

* Update Entra plugin test with SSO connector field

* Fix lint

* Remove leftover panics

* Adjust success message

* Downgrade log message level

* Expect exactly 1 SP for MS Graph, improve errors

* Properly extract hostname for enterprise app name

* Comment on assuming the first subscription

* Address review nits

* Factor out sso info fetch into a function

* fixup refactor

* Add retry logic to app role assignment

* Make godoc conventional
github-merge-queue Bot pushed a commit that referenced this pull request Jun 7, 2024
* Entra ID reconciler: directory reconciler prerequisites (#40778)

* Add Entra ID resource origin

* Ignore ID and Revision from `header` in cmp

* Add e_imports for MS Graph SDK

* Entra ID integration: add proto definitions (#40997)

* Entra ID integration boilerplate (#40998)

* Add e imports for MS Graph SDK

* Add ability to sign Entra ID OIDC JWTs, rework KID handling

- Synthesize Key IDs for our JWT keys. For backwards compatibility, also
  include the same keys with an empty `kid` in JWKS.
- Sign AWS OIDC tokens with a `kid=""` header claim,
  rather than omitting the `kid` claim altogether.
  See comment for details.

* Add validation for Entra ID plugin

* Fix typo in assertion function name

* Update the OIDC JWKS test to expect the same key twice

* Add Entra ID plugin type constant

* go mod tidy

* Fix expected JWKS size in integration test

* Add basic tests for KeyID

* Move Azure auth settings from Plugin to Integration

* Address review comments

* Add a unit test to ensure KeyID compatibility

* Add license header to token_generator.go

* Rename validation function per new conventions

* Access Graph: sync AWS identity providers  (#41368)

* Add AWSSAMLProviderV1 to access graph proto

* Access Graph: sync AWS SAML Providers

* Parse SAML entity descriptor before sending to TAG

* Add protos for AWS OIDC providers

* Fetch AWS OIDC providers

* Fetch signing certificates for AWS SAML providers

* Deflake identity provider fetch test

The concrete implementation of IAM mock uses a map,
resulting in non-deterministic iteration order.
Sort the results before comparing to alleviate.

* Update lib/srv/discovery/fetchers/aws-sync/iam_test.go

Co-authored-by: Jakub Nyckowski <jakub.nyckowski@goteleport.com>

---------

Co-authored-by: Jakub Nyckowski <jakub.nyckowski@goteleport.com>

* Access Graph: Entra ID application sync prerequisites (#41650)

* Add access graph settings to Entra ID plugin

* Move Entra ID labels to OSS

* Add Entra resources and RPC to Access Graph proto

* Add azure-oidc integration to web.

Current code assumes that Integration is always either AwsOidc,
or an external audit storage integration

* Change app sso cache to a repeated field

* Entra ID integration: add onboarding script (#41811)

* Add Entra ID integration onboarding script

* Adapt after proto update

* Validate names in azure script handler, add test

* Add license headers

* Update Entra plugin test with SSO connector field

* Fix lint

* Remove leftover panics

* Adjust success message

* Downgrade log message level

* Expect exactly 1 SP for MS Graph, improve errors

* Properly extract hostname for enterprise app name

* Comment on assuming the first subscription

* Address review nits

* Factor out sso info fetch into a function

* fixup refactor

* Add retry logic to app role assignment

* Make godoc conventional

* Entra ID integration: integration script updates and web onboarding prerequisites (#42172)

* Remove integration name validation from web script

Not used by the script. It is validated by the "plugins/validate"
endpoint.

* Add required frontend constants for Entra ID

* Support Azure/Entra integrations in the list

* Add IsPolicyEnabled to web config

* Allow custom URL for ButtonLockedFeature

* Add CTA_ENTRA_ID event type

* Expose TAGInfoCache for use in e

* Add LackingIgs option

* Add Entra ID icon

* Add Entra ID plugin to storybook

* Bump e for dev build

* Return underlying error in getPrivateAPIToken

* Find default Azure subscription instead of the first one

* Require user to re-login when provisioning Azure OIDC

* Update prehog protos with Entra ID values

From https://github.com/gravitational/cloud/pull/9111

* Suppress verbose warnings / information from az

* Add an additional message after successful auth

Lets user know that `az login` has completed
and `teleport` is continuing its work.

* Move EntraId constant to the bottom

* Revert unintended changes to usageevents

CTA is 1-to-1 with prehog, but IntegrationEnrollKind is not.

* Remove integrationName validation asserts from test

This parameter is no longer accepted by the endpoint

* Revert "Bump e for dev build"

This reverts commit fc747a0.

* `go mod tidy` secondary modules

---------

Co-authored-by: Jakub Nyckowski <jakub.nyckowski@goteleport.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-changelog Indicates that a PR does not require a changelog entry

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants