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

Add a flag to control whether credentials are printed during bootstrapping #461

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

eric-maynard
Copy link
Contributor

Description

This adds a new flag, BOOTSTRAP_PRINT_CREDENTIALS, that controls whether the bootstrap command prints root credentials to stdout.

If it's disabled, and environment variables were not provided to set the root credentials, bootstrapping will fail.

Fixes #450

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • Documentation update
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Credentials are now printed during bootstrap when it's enabled:

realm: default-realm root principal credentials: 2b98107557bcce20:f74281319ac8519ef30cbced6563223b

@@ -181,6 +196,19 @@ private PrincipalSecretsResult bootstrapServiceAndCreatePolarisPrincipalForRealm
throw new IllegalArgumentException(overrideMessage);
}

// TODO rebase onto #422, call a method like PrincipalSecretsGenerator.hasEnvironmentVariables
Copy link
Contributor

Choose a reason for hiding this comment

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

idea: maybe pass a flag down to PrincipalSecretsGenerator to not use random secrets if printCredentials is false? Then the PrincipalSecretsGenerator can simply throw if the specific realm/user combination is missing env. vars. WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I like that idea. If there is a good pathway from the bootstrap command down to the PrincipalSecretsGenerator then I think that works as well. It should hopefully be more clear when #422 merges.

@eric-maynard
Copy link
Contributor Author

eric-maynard commented Nov 25, 2024

Hey @dimas-b, do you mind taking a look now that #422 has merged?

I think the integration is easy enough with some slight refactoring to PrincipalSecretsGenerator.

I left the current behavior wrt. using env variables even when printing is enabled, since if that's what the user decides to explicitly configure we can respect it. In the worst case we are just echoing env variables.

@@ -82,4 +86,28 @@ static PrincipalSecretsGenerator bootstrap(String realmName, Function<String, St
}
};
}

/** Return true if environment variables for client ID & secret are set */
static boolean hasCredentialVariables(String realmName, String principalName) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Exposing this static method does not look nice to be from the encapsulation point of view. I'd prefer to keep credential generation logic inside PrincipalSecretsGenerator instances.

We already have the bootstrap flag in LocalPolarisMetaStoreManagerFactory that controls secret generation under a synchronization lock. WDYT about converting it into something like PrincipalSecretsGenerator generatorFallback and making PrincipalSecretsGenerator.bootstrap() take an explicit fallback parameter (which is currently always RANDOM_SECRETS)? LocalPolarisMetaStoreManagerFactory would use RANDOM_SECRETS as the fallback by default, but during bootstrapping, we could have a custom impl. that produces random secrets if printing is enabled, but throws an exception if fallback if requested, but printing is disabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The issue is that the check against POLARIS_BOOTSTRAP_%s_%s_CLIENT_SECRET was buried inside bootstrap. One potential fix could be to sort of refactor PrincipalSecretsGenerator so that we have multiple implementations / providers and we could then use the type of the PrincipalSecretsGenerator to determine whether the credentials are user-provided or generated.

Comment on lines +111 to +119
if (this.printCredentials(polarisContext)) {
String msg =
String.format(
"realm: %1s root principal credentials: %2s:%3s",
realmContext.getRealmIdentifier(),
secretsResult.getPrincipalSecrets().getPrincipalClientId(),
secretsResult.getPrincipalSecrets().getMainSecret());
System.out.println(msg);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this logic belongs to the secrets generator. The MetaStoreManager doesn't need to know anything about whether the secrets generated are provided by the user or if they've been generated randomly. So why would it be concerned with printing the credentials? The secrets generator knows if the secrets were provided explicitly or if they were randomly generated.

I think the bootstrap command should take a print-credentials config flag and the constructed secrets generator can react accordingly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It seems like the PrincipalSecretsGenerator is doing exactly what the name suggests: generating secrets.

Whatever is done with those secrets -- persisting them, using them, printing them -- is outside the purview of the generator itself.

You are right that the MetaStoreManager doesn't need to know anything about printing either (it doesn't in this PR) and clearly this should be outside the purview of the metastore itself.

And so we landed on the factory. I would be happy to take this bootstrapping logic and excise it to somewhere more idiomatic if that is a concern. But right now the bootstrapping logic lives here (e.g. the purge check) and this seems like the most appropriate place that doesn't change the responsibility of either the metastore or generator classes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looking again, is your objection specifically to the protected method printCredentials?

That only exists to support the legacy behavior of the in-memory metastore always printing credentials, and if possible I would very much be in favor of removing that.

However it feels like pushing that logic down into an existing method (whether secretsGenerator, createMetaStoreSession, or elsewhere) could be a bit hacky if it winds up somewhere it doesn't belong.

Comment on lines 214 to 224
boolean environmentVariableCredentials =
PrincipalSecretsGenerator.hasCredentialVariables(
realmContext.getRealmIdentifier(), PolarisEntityConstants.getRootPrincipalName());
if (!this.printCredentials(polarisContext) && !environmentVariableCredentials) {
String failureMessage =
String.format(
"It appears that environment variables were not provided for root credentials, and that printing "
+ "the root credentials is disabled via %s. If bootstrapping were to proceed, there would be no way "
+ "to recover the root credentials",
PolarisConfiguration.BOOTSTRAP_PRINT_CREDENTIALS.key);
LOGGER.error("\n\n {} \n\n", failureMessage);
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar here - why is the metastore aware of whether the secrets were provided by environment variables? What if there are other impls of secrets generators that don't rely on env variables? E.g., we could have one that calls AWS SecretsManager to dynamically generate and store the secrets without any env variables. Should this code throw an exception?


String clientId = config.apply(propId.toUpperCase(Locale.ROOT));
Copy link
Contributor

Choose a reason for hiding this comment

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

Did we lose uppercasing in the new code?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I actually think this is wrong isn't it? It seems like you can have both a dimas and a DIMAS user, so how would you differentiate them in the env variables?

This is assuming we now allow the use of env variables for non-root users.

* @return A {@link PrincipalSecretsGenerator} that can generate secrets through `produceSecrets`
*/
public static PrincipalSecretsGenerator bootstrap(String realmName) {
return new DefaultPrincipalSecretsGenerator(realmName);
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: maybe rename DefaultPrincipalSecretsGenerator -> BootstrapPrincipalSecretsGenerator?.. it is not actually default in LocalPolarisMetaStoreManagerFactory

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. It's just a wrapper, so I was not sure what to call it. Since it's only used during bootstrap, let me change to BootstrapPrincipalSecretsGenerator for now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[BUG] Potential eclipselink schema upgrade issue
3 participants