Skip to content

Append router CA to cluster CA in kubeconfig #1242

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

Merged

Conversation

sallyom
Copy link
Contributor

@sallyom sallyom commented Feb 13, 2019

With users created with an identity-provider, OAuth flow goes through router and router-ca is not trusted. This prohibits the user from using oc login from command line, without manually appending the router ca to the cluster ca. The openshift-ingress-operator creates the router-CA and this is not available until the very end of an install.

This PR adds the router CA from configmap router-ca -n openshift-config-managed to the kubeconfig certificate-authority-data. This allows an identity-provided user to oc login from the terminal.

@openshift-ci-robot openshift-ci-robot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Feb 13, 2019
Copy link
Contributor

@staebler staebler left a comment

Choose a reason for hiding this comment

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

Can you add some context to the commit message describing the motivation behind this change?

routerSecret, err := client.CoreV1().Secrets("openshift-ingress-operator").Get("router-ca", metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
return nil
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we really want to silently ignore a missing router-ca 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.

I will check, but I think there's a possible expectation that it won't be created.

Copy link
Contributor

Choose a reason for hiding this comment

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

The secret will always be created. However, I'd suggest using the router-ca configmap in the openshift-config-managed namespace, which will be created when some clusteringress is using the operator-generated default certificate generated using that CA certificate, and will be absent otherwise.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thanks @Miciah will do!

Choose a reason for hiding this comment

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

but also, at the very least log the error. preferably don't swallow it at all.

Copy link
Contributor Author

@sallyom sallyom Feb 13, 2019

Choose a reason for hiding this comment

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

I've added a log msg for that error

Copy link
Member

Choose a reason for hiding this comment

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

Won't we want to poll/wait until the config-map has a non-empty CA instead of giving up?

return err
}
routerCrtBytes := routerSecret.Data["tls.crt"]
absDir, err := filepath.Abs(directory)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need to use the absolute directory?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no we do not, fixed

return err
}
var kconfig *clientcmdv1.Config
err = yaml.Unmarshal(yml, &kconfig)
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be nice to grab the kubeconfig.Admin asset from the store, which would already have the unmarshaled config. Unfortunately, (1) the asset store used has already gone out of scope, and (2) there is currently no way of fetching from the store without purging the other assets from disk.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

right, I pondered that

}

clusterCABytes := kconfig.Clusters[0].Cluster.CertificateAuthorityData
appendedCA := string(routerCrtBytes) + string(clusterCABytes)
Copy link
Contributor

Choose a reason for hiding this comment

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

"Appended" is not quite the right word here since you are adding the router CA to the beginning not the end.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

correct, thanks, i'll rename : )

Choose a reason for hiding this comment

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

You can use clientcmd.LoadFromFile and clientcmd.WriteToFile . Might want to use certpool helpers to concat the CAs as well

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah, awesome, thanks

if apierrors.IsNotFound(err) {
return nil
}
return err
Copy link
Contributor

Choose a reason for hiding this comment

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

Wrap the error here and elsewhere in this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@sallyom sallyom changed the title WIP: append router CA to cluster CA in kubeconfig Append router CA to cluster CA in kubeconfig Feb 13, 2019
@openshift-ci-robot openshift-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Feb 13, 2019
@@ -116,6 +119,10 @@ var (
logrus.Fatal(err)
}

if err = appendRouterCAToClusterCA(config, rootOpts.dir); err != nil {
logrus.Fatal(err)

Choose a reason for hiding this comment

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

Should this be fatal or only a warning? I thinking since it doesn't block accessing the cluster, but maybe it's ok to fail since this is the last step?

Copy link
Member

Choose a reason for hiding this comment

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

Should this be fatal or only a warning?

I'm fine failing fast. We can relax later if necessary, but unless this is fatal by default we don't get CI coverage (or will e2e start failing without this step?).

routerSecret, err := client.CoreV1().Secrets("openshift-ingress-operator").Get("router-ca", metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
return nil

Choose a reason for hiding this comment

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

but also, at the very least log the error. preferably don't swallow it at all.

return err
}

clusterCABytes := kconfig.Clusters[0].Cluster.CertificateAuthorityData

Choose a reason for hiding this comment

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

check that kconfig != nil and len(kconfig.Clusters) > 0 before accessing nested fields

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done, and outdated here

}

clusterCABytes := kconfig.Clusters[0].Cluster.CertificateAuthorityData
appendedCA := string(routerCrtBytes) + string(clusterCABytes)

Choose a reason for hiding this comment

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

You can use clientcmd.LoadFromFile and clientcmd.WriteToFile . Might want to use certpool helpers to concat the CAs as well

@sallyom sallyom force-pushed the append-router-ca-to-kc branch 2 times, most recently from 83ed4f6 to 2624e1b Compare February 13, 2019 22:44
@abhinavdahiya
Copy link
Contributor

abhinavdahiya commented Feb 13, 2019

The kubeconfig that you are adding the router ca to has admin credentials, and since you cannot distribute that kubeconfig to a user that is trying to oc login. So i'm still unclear on why this PR is required trying to solve?

I tried to login following the gist

$ unset KUBECONFIG
$ oc login adahiya-1-api.devcluster.openshift.com:6443 --v=6
I0213 14:57:06.751825    7586 round_trippers.go:405] HEAD https://adahiya-1-api.devcluster.openshift.com:6443/  in 273 milliseconds
The server uses a certificate signed by an unknown authority.
You can bypass the certificate check, but any data you send to the server could be intercepted by others.
Use insecure connections? (y/n): y

I0213 14:57:11.749795    7586 round_trippers.go:405] GET https://adahiya-1-api.devcluster.openshift.com:6443/.well-known/oauth-authorization-server 200 OK in 304 milliseconds
I0213 14:57:12.122486    7586 round_trippers.go:405] GET https://openshift-authentication-openshift-authentication.apps.adahiya-1.devcluster.openshift.com/oauth/authorize?client_id=openshift-challenging-client&code_challenge=pQE6QaNhx1d-qtBSA-Wer9BepDDep_Zqn8oT3KnS9mM&code_challenge_method=S256&redirect_uri=https%3A%2F%2Fopenshift-authentication-openshift-authentication.apps.adahiya-1.devcluster.openshift.com%2Foauth%2Ftoken%2Fimplicit&response_type=code 400 Bad Request in 372 milliseconds
I0213 14:57:12.123212    7586 helpers.go:201] server response object: [{
  "metadata": {},
  "status": "Failure",
  "message": "Internal error occurred: unexpected response: 400",
  "reason": "InternalError",
  "details": {
    "causes": [
      {
        "message": "unexpected response: 400"
      }
    ]
  },
  "code": 500
}]
F0213 14:57:12.123312    7586 helpers.go:119] Error from server (InternalError): Internal error occurred: unexpected response: 400

It looks like with the current setup oc login client requires you to trust 2 servers kube-apiserver and oauth server using the route.
So IMO what makes sense to me is:

  • either creating a bundle that has CAs for both and telling users to use that for --certificate-authority
  • or creating a special kubeconfig with apiserver url and CA set to the bundle..

@sallyom sallyom force-pushed the append-router-ca-to-kc branch from 2624e1b to 40311e5 Compare February 13, 2019 23:58
@ericavonb
Copy link

ericavonb commented Feb 14, 2019

The kubeconfig that you are adding the router ca to has admin credentials, and since you cannot distribute that kubeconfig to a user that is trying to oc login. So i'm still unclear on why this PR is required trying to solve?

This PR makes it so that an administrator can test their IDP configuration using the installer generated kubeconfig. For letting other users access the cluster, the admin will need to provide them with the CA bundle.

It looks like with the current setup oc login client requires you to trust 2 servers kube-apiserver and oauth server using the route.
Yes, that is correct. You need to trust the kube-apiserver for using the api and discovering the auth server, and you need to trust the auth server to securely authenticate according to the oauth spec.

* either creating a bundle that has CAs for both and telling users to use that for `--certificate-authority`
* or creating a special kubeconfig with `apiserver url` and CA set to the bundle..

The cluster data in the kubeconfig is meant to store that context. I think it's the appropriate place to put it.

@sallyom sallyom force-pushed the append-router-ca-to-kc branch 2 times, most recently from 354a8b6 to fcef841 Compare February 14, 2019 02:01
@sallyom
Copy link
Contributor Author

sallyom commented Feb 14, 2019

  • either creating a bundle that has CAs for both and telling users to use that for --certificate-authority

That would work, but would be a major shift from 3.x. People expect to be able to just
oc login -u user -p password or oc login <server> --token blah <--not sure where we stand on that atm

@sallyom sallyom force-pushed the append-router-ca-to-kc branch from fcef841 to 0afd02b Compare February 14, 2019 18:29
@sallyom
Copy link
Contributor Author

sallyom commented Feb 14, 2019

/refresh

@sallyom
Copy link
Contributor Author

sallyom commented Feb 14, 2019

/test e2e-aws

return errors.New("kubeconfig CertificateAuthorityData not found")
}
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(clusterCABytes) {
Copy link

@mrogers950 mrogers950 Feb 14, 2019

Choose a reason for hiding this comment

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

You're using this just for a parsing check, correct? If so, the error should say something about the CA being not in valid PEM format.

Copy link
Contributor Author

@sallyom sallyom Feb 14, 2019

Choose a reason for hiding this comment

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

correct, thanks, I've updated the error msg

@sallyom sallyom force-pushed the append-router-ca-to-kc branch 2 times, most recently from 29cc938 to 5d0e43b Compare February 15, 2019 15:15
@sallyom sallyom force-pushed the append-router-ca-to-kc branch from 5d0e43b to 4033577 Compare February 15, 2019 15:22
Copy link

@ericavonb ericavonb left a comment

Choose a reason for hiding this comment

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

/lgtm

@abhinavdahiya
Copy link
Contributor

@sallyom can you update the commit message
https://github.com/openshift/installer/blob/master/CONTRIBUTING.md#commit-message-format

Then we lgtm and merge

}

routerCrtBytes := []byte(caConfigMap.Data["ca-bundle.crt"])
kubeconfig := filepath.Join(directory, "auth", "kubeconfig")
Copy link
Member

Choose a reason for hiding this comment

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

Ideally we'd hold this in memory, but this is fine short-term.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes I think there's a FIXME already elsewhere in this file for that

return errors.Wrap(err, "loading kubeconfig")
}

if kconfig == nil || len(kconfig.Clusters) == 0 {
Copy link
Member

Choose a reason for hiding this comment

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

I think we want:

if kconfig == nil || len(kconfig.Clusters) != 1 {
  return errors.New("invalid kubeconfig")
}

which will also allow us to drop the following loop over Clusters. We will be able to drop this guard once we switch to keeping the kubeconfig in memory.

Copy link
Contributor Author

@sallyom sallyom Feb 15, 2019

Choose a reason for hiding this comment

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

I'll open a follow-up? I could not find how to get this string:

// Clusters is a map of referencable names to cluster configs
 Clusters map[string]*Cluster 

here

referencable names to cluster configs? What is that reference-able name? I'm happy to do some testing (and open a PR to clarify for others) to figure that out (or maybe someone knows and can tell me). But if there_were_ more than 1 cluster I'd want to add to each, so the != 0 seems ok here. If not I can also include that in the follow-up PR.

@abhinavdahiya
Copy link
Contributor

/approve

@openshift-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: abhinavdahiya, ericavonb, sallyom

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci-robot openshift-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Feb 15, 2019
@openshift-merge-robot openshift-merge-robot merged commit 35c81e1 into openshift:master Feb 15, 2019
@wking
Copy link
Member

wking commented Feb 19, 2019

Hmm. Does this mean "we never rotate the router CA"? Because if we did, it would break oc login with this kubeconfig, right?

@sallyom
Copy link
Contributor Author

sallyom commented Feb 22, 2019

@wking yes, correct, a change in router-CA will break oc login unless kubeconfig is modified. Atm, cluster-ingress-operator does not rotate certificates.

@enj
Copy link
Contributor

enj commented Feb 23, 2019

Hmm. Does this mean "we never rotate the router CA"? Because if we did, it would break oc login with this kubeconfig, right?

Yes, and this is expected. This change is only expected to handle the OOTB experience. oc honors your system built in roots, so it work if the router CA is replaced as long as it is a globally valid cert or the admin has arranged for the cert to be trusted within the corporate environment (the latter is what I expect to be the common case).

wking added a commit to wking/openshift-installer that referenced this pull request Apr 5, 2019
This was added in 4033577 (Append router CA to cluster CA in
kubeconfig, 2019-02-12, openshift#1242), where the motivation was [1]:

  With users created with an identity-provider, OAuth flow goes
  through router and router-ca is not trusted. This prohibits the user
  from using oc login from command line, without manually appending
  the router ca to the cluster ca. The openshift-ingress-operator
  creates the router-CA and this is not available until the very end
  of an install.

  This PR adds the router CA from configmap router-ca -n
  openshift-config-managed to the kubeconfig
  certificate-authority-data. This allows an identity-provided user to
  oc login from the terminal.

But the admin kubeconfig is already authenticated, so folks shouldn't
be using 'oc login' with that kubeconfig.  For example, see dee6929
(Modify kubeadmin usage message, admins should not use kubeadmin via
CLI, 2019-04-01, openshift#1513).  That leaves us with no use case for this
modification, and making the finish code watch-only sets us up to
rename away from the user-provided-infrastructure subcommand.

[1]: openshift#1242 (comment)
wking added a commit to wking/openshift-installer that referenced this pull request Aug 1, 2022
The console may become optional [1], so teach the installer to handle
its absence gracefully.

We've waited on the console since way back in ff53523 (add logs at
end of install for kubeadmin, consoleURL, 2018-12-06, openshift#806).  Back
then, install-complete timing was much less organized, and since
e17ba3c (cmd: wait for the cluster to be initialized, 2019-01-25, openshift#1132)
we've blocked on ClusterVersion going Available=True. So the current
dependency chain is:

1. Console route admission blocks console operator from going
   Available=True in its ClusterOperator.
2. Console ClusterOperator blocks cluster-version operator from
   going Available=True in ClusterVersion.
3. ClusterVersion blocks installer's waitForInitializedCluster.

So we no longer need to wait for the route to show up, and can fail
fast if we get a clear IsNotFound.  I'm keeping a bit of polling so we
don't fail an install on a temporary network hiccup.

We don't want to drop the console check entirely, because when it is
found, we want:

* To continue to log that access pathway on install-complete.
* To continue to append the router CA to the kubeconfig.

That latter point has been done since 4033577 (append router CA to
cluster CA in kubeconfig, 2019-02-12, openshift#1242).  The motivation in that
commit message is not explicit, but the idea is to support folks who
naively run 'oc login' with the kubeadmin kubeconfig [2] (despite that
kubeconfig already having cluster-root access) when the console
route's cert's CA happens to be something that the user's local trust
store doesn't include by default.

[1]: openshift/enhancements#922
[2]: openshift#1541 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged. size/M Denotes a PR that changes 30-99 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants