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

Oidc autoregistration #152

Merged
merged 25 commits into from
Jun 13, 2020
Merged

Conversation

brutif
Copy link
Contributor

@brutif brutif commented May 8, 2020

What this PR does / why we need it?:
Adds ability for oidc sso clients to automatically register with RedHat Single Sign-On
Does this PR introduce a user-facing change? yes

  • User guide
  • CHANGELOG.md

Which issue(s) this PR fixes:
Addresses kabanero-io/kabanero-security#71

#
# Optional: To skip TLS certificate checking with the provider, specify insecureTLS as true. Default is false.
# insecureTLS: dHJ1ZQ==
----
Copy link
Contributor

Choose a reason for hiding this comment

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

can we also add what the corresponding RuntimeComponent CR would be, and how it references this Secret? It would be good to visualize a working sample of what fields are needed in this auto-registration flow. Is there a specific naming convention that one must follow for this Secret to be auto discovered, or it can be named whatever we want as long as we reference it from the CR?

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 secret is an attribute of the OpenLibertyApplication, sso.oidc[].autoRegisterSecret, so it's not usable from the RuntimeComponent CR. It can be named whatever we want. I've updated the user guide to make the flow and who needs what (RedHat SSO vs. IBM Cloud Identity) more clear.

Copy link
Contributor

Choose a reason for hiding this comment

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

my bad - I meant to say the corresponding OpenLibertyApplication CR sample, that shows a complete CR using autoRegisterSecret. I really like the Secret example, I think we just need the corresponding olapp CR.

Copy link
Contributor Author

@brutif brutif May 28, 2020

Choose a reason for hiding this comment

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

ah, got it. Added an example.

@arthurdm
Copy link
Contributor

arthurdm commented Jun 1, 2020

thanks @brutif - the doc / high level changes looks good to me. I will defer to @leochr for the code changes review.

Copy link
Member

@leochr leochr left a comment

Choose a reason for hiding this comment

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

@brutif Added some initial comments. Yet to review some code. Thanks.

secretName := instance.GetName() + ssoSecretNameSuffix
ssoSecret := &corev1.Secret{}
err := r.GetClient().Get(context.TODO(), types.NamespacedName{Name: secretName, Namespace: instance.GetNamespace()}, ssoSecret)
err = lutils.CustomizeEnvSSO(&deploy.Spec.Template, instance, r.GetClient(), r.IsOpenShift())
Copy link
Member

Choose a reason for hiding this comment

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

should reference statefulSet's template

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will be fixed

if err != nil {
return errors.Wrapf(err, "Secret for Single sign-on (SSO) was not found. Create a secret named %q in namespace %q with the credentials for the login providers you selected in application image.", secretName, instance.GetNamespace())
reqLogger.Error(err, "Failed to reconcile Single sign-on configuration")
return err
Copy link
Member

Choose a reason for hiding this comment

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

Use ManageError to handle error:
r.ManageError(err, common.StatusConditionTypeReconciled, instance)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is inside the mutator function, the enclosing createOrUpdate call returns it, it's then handled with a call to r.ManageError outside of that around line 666, so ManageError is getting called.

| `sso.github.hostname` | Specifies the host name of your enterprise GitHub, such as _github.mycompany.com_. The default is _github.com_, which is the public Github.
| `sso.oidc` | The list of OpenID Connect (OIDC) providers to authenticate with. Required fields: _discoveryEndpoint_. Specify sensitive fields, such as _clientId_ and _clientSecret_, by using the `Secret`.
| `sso.oidc[].autoRegisterSecret` | Specifies name of secret containing information to automatically register Liberty with the OIDC provider. See link:++#Using-automatic-registration-with-OIDC-providers++[Using automatic registration with OIDC providers].
Copy link
Member

Choose a reason for hiding this comment

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

Should it be "Liberty application"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@@ -315,6 +318,66 @@ spec:
name: self-signed
termination: reencrypt
----
==== Using automatic registration with OIDC providers

The operator can request a client Id and client Secret from providers, rather than requiring them in advance. This can simplify deployment, as the provider's administrator can supply the information needed for registration once, instead of supplying clientIds and secrets repetitively. An additional secret specified by `sso.oidc[].autoRegisterSecret` contains additional information necessary to perform the registration. First the operator will make an https request to the `sso.oidc[].discoveryEndpoint` to obtain URLs for subsequent REST calls. Next it will make additional REST calls to the provider and obtain a client Id and client Secret. Automatic registration takes precedence over the values in the sso secret. The name of this secret should end with `-olapp-sso` for it to be monitored for changes. This is tested with Red Hat Single Sign-on (RHSSO).
Copy link
Member

@leochr leochr Jun 5, 2020

Choose a reason for hiding this comment

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

"The name of this secret should end with -olapp-sso for it to be monitored for changes."
With the current implementation you have in place, I don't think the auto-register secret is monitored.

Instead of users always having to explicitly specify the auto-register secret name, by default we could automatically find it (only while reconciling providers) using a naming convention: <CR_name>-olapp-sso-<provider_id>

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 will look into auto-detecting off of the name, to make it consistent with the way we handle the other sso secret. I can't think of any disadvantage to that.

ssoEnv = append(ssoEnv, *createEnvVarSSO(id, "_CLIENTID", clientId))
ssoEnv = append(ssoEnv, *createEnvVarSSO(id, "_CLIENTSECRET", clientSecret))
} else {
if !haveSsoSecret { // if we don't have an autoreg secret, we need an sso secret.
Copy link
Member

Choose a reason for hiding this comment

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

A similar error is needed for OAuth2 providers below and GitHub above (when Github.Hostname is specified).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

}
clientId, clientSecret, err = RegisterWithOidcProvider(regData, regSecret)
if err != nil {
return errors.Wrapf(err, "Error occured during registration with OIDC provider")
Copy link
Member

Choose a reason for hiding this comment

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

It'll be good to add the provider ID here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

return errors.Wrapf(err, "Registration Data Secret for Single sign-on (SSO) of name %q was not found. Create a secret in namespace %q with the credentials for the login providers you selected in application image.", secretName, instance.GetNamespace())
}
// if no clientId specified for this provider, try auto-registration
clientId := string(ssoSecret.Data[strings.ToLower(id) + "-clientId"])
Copy link
Member

Choose a reason for hiding this comment

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

There could be mismatch due to converting to lower case. oidcClient.ID should be used as is here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

return errors.Wrapf(err, "Error occured during registration with OIDC for provider " + providerId)
}

_, err = controllerutil.CreateOrUpdate(context.TODO(), client, ssoSecret, func() error {
Copy link
Member

Choose a reason for hiding this comment

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

Updating the secret for each provider will cause many reconciliations unnecessarily. It'll be good to collect the data to add to the secret and then update the secret just once at the end. That way it'll have to reconcile only once.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

ssoSecret.Data[strings.ToLower(id) + autoregFragment + "RegisteredOidcClientId"] = []byte(clientId)
ssoSecret.Data[strings.ToLower(id) + autoregFragment + "RegisteredOidcSecret"] = []byte(clientSecret)
ssoSecret.Data[strings.ToLower(id) + "-clientId"] = []byte(clientId)
ssoSecret.Data[strings.ToLower(id) + "-clientSecret"] = []byte(clientSecret)
Copy link
Member

Choose a reason for hiding this comment

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

is the clientSecret already base64 encoded at this point? or will it be automatically converted when adding to secret?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

kube appears to decode on read and encode on write, so we don't have to.

"github.com/OpenLiberty/open-liberty-operator/pkg/apis/openliberty/v1beta1.ServiceBindingConsumes": schema_pkg_apis_openliberty_v1beta1_ServiceBindingConsumes(ref),
"github.com/OpenLiberty/open-liberty-operator/pkg/apis/openliberty/v1beta1.ServiceBindingProvides": schema_pkg_apis_openliberty_v1beta1_ServiceBindingProvides(ref),
"github.com/OpenLiberty/open-liberty-operator/pkg/apis/openliberty/v1beta1.StatusCondition": schema_pkg_apis_openliberty_v1beta1_StatusCondition(ref),
"github.com/open-liberty-operator/pkg/apis/openliberty/v1beta1.GithubLogin": schema_pkg_apis_openliberty_v1beta1_GithubLogin(ref),
Copy link
Member

Choose a reason for hiding this comment

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

The path changed for some reason. Likely due to go path setting on your machine.

Copy link
Member

@leochr leochr left a comment

Choose a reason for hiding this comment

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

@brutif Looks good. We can merge this now. Thank you!

@brutif brutif merged commit 5a2cb25 into OpenLiberty:master Jun 13, 2020
brutif added a commit to brutif/open-liberty-operator that referenced this pull request Jun 19, 2020
* update userguide for auto registration

* add registration code

* update types

* clean up format

* propagate error

* interim updates

* rework route logic

* clean up

* merge with integration

* fix user-guide table

* remove commented out code

* minor doc clarifications

* improve rhsso secret explanation

* add liberty cr usage example

* move registration results from status to the secret

* update for code review comments

* add updated crd

* remove refs to autoreg secret that is no longer used

* adjust registratiton json for IBM Cloud Verify

* fix setting env var twice

* updates for code review, change client name registered at provider

* resolve merge conflict
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.

3 participants