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

migrate group approver to use subject access reviews #45619

Merged
merged 2 commits into from
May 31, 2017

Conversation

mikedanese
Copy link
Member

@mikedanese mikedanese commented May 10, 2017

WIP, needs test and changes to kubeadm

depends on #45514

kube-controller-manager has dropped support for the `--insecure-experimental-approve-all-kubelet-csrs-for-group` flag. Instead, the `csrapproving` controller uses authorization checks to determine whether to approve certificate signing requests:
* requests for a TLS client certificate for any node are approved if the CSR creator has `create` permission on the `certificatesigningrequests` resource and `nodeclient` subresource in the `certificates.k8s.io` API group
* requests from a node for a TLS client certificate for itself are approved if the CSR creator has `create` permission on the `certificatesigningrequests` resource and the `selfnodeclient` subresource in the `certificates.k8s.io` API group
* requests from a node for a TLS serving certificate for itself are approved if the CSR creator has `create` permission on the `certificatesigningrequests` resource and the `selfnodeserver` subresource in the `certificates.k8s.io` API group

@mikedanese mikedanese self-assigned this May 10, 2017
@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label May 10, 2017
@k8s-reviewable
Copy link

This change is Reviewable

@k8s-github-robot
Copy link

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: mikedanese

Needs approval from an approver in each of these OWNERS Files:

You can indicate your approval by writing /approve in a comment
You can cancel your approval by writing /approve cancel in a comment

@k8s-github-robot k8s-github-robot added approved Indicates a PR has been approved by an approver from all required OWNERS files. kind/api-change Categorizes issue or PR as related to adding, removing, or otherwise changing an API size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. release-note-label-needed labels May 10, 2017
@@ -185,7 +185,7 @@ func (s *CMServer) AddFlags(fs *pflag.FlagSet, allControllers []string, disabled
fs.StringVar(&s.ServiceAccountKeyFile, "service-account-private-key-file", s.ServiceAccountKeyFile, "Filename containing a PEM-encoded private RSA or ECDSA key used to sign service account tokens.")
fs.StringVar(&s.ClusterSigningCertFile, "cluster-signing-cert-file", s.ClusterSigningCertFile, "Filename containing a PEM-encoded X509 CA certificate used to issue cluster-scoped certificates")
fs.StringVar(&s.ClusterSigningKeyFile, "cluster-signing-key-file", s.ClusterSigningKeyFile, "Filename containing a PEM-encoded RSA or ECDSA private key used to sign cluster-scoped certificates")
fs.StringVar(&s.ApproveAllKubeletCSRsForGroup, "insecure-experimental-approve-all-kubelet-csrs-for-group", s.ApproveAllKubeletCSRsForGroup, "The group for which the controller-manager will auto approve all CSRs for kubelet client certificates.")
fs.BoolVar(&s.EnableKubeletCSRApprover, "enable-kubelet-csr-approver", s.EnableKubeletCSRApprover, "Enable the kubelet CSR approver which approves kubelet CSRs based on subject access reviews")
Copy link
Member Author

Choose a reason for hiding this comment

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

Do we need an option for this or can we just leave it on?

Copy link
Member

@liggitt liggitt May 15, 2017

Choose a reason for hiding this comment

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

When the auto-approving controller is its own controller, you would use --controllers=... to decide whether to run it or not. I wouldn't enable it by default yet. For example, if it was named autoapprove-kubelet-csrs and was not on by default, you would specify --controllers=*,autoapprove-kubelet-csrs to run all on-by-default controllers and the autoapprover.

Copy link
Member

Choose a reason for hiding this comment

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

oh, I see this already splits it out

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok, I'll drop it.

@liggitt liggitt self-assigned this May 11, 2017
@@ -311,7 +311,8 @@ func NewControllerInitializers() map[string]InitFunc {
controllers["disruption"] = startDisruptionController
controllers["statefuleset"] = startStatefulSetController
controllers["cronjob"] = startCronJobController
controllers["certificatesigningrequests"] = startCSRController
controllers["certificatesigningrequestssigning"] = startCSRSigningController
controllers["certificatesigningrequestsapproving"] = startCSRApprovingController
Copy link
Member

Choose a reason for hiding this comment

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

Changing the controller name needs a release note since people can address it via --controllers. If we're changing it, shorter might be better? Something like csrsigner / csrautoapprover?

ApproveAllKubeletCSRsForGroup string
// enableKubeletCSRApprover tells the CSR controller to approve
// kubelet CSRs based on SubjectAccessReviews.
EnableKubeletCSRApprover bool
Copy link
Member

Choose a reason for hiding this comment

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

not needed, use controller name in Controllers

utilruntime.HandleError(fmt.Errorf("unable to parse csr %q: %v", csr.Name, err))
return csr, nil
}
if !reflect.DeepEqual([]string{"system:nodes"}, x509cr.Subject.Organization) {
Copy link
Member

Choose a reason for hiding this comment

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

I wouldn't expect this to be true for node serving certs

Copy link
Member Author

Choose a reason for hiding this comment

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

if !reflect.DeepEqual([]string{"system:nodes"}, x509cr.Subject.Organization) {
return csr, nil
}
if len(x509cr.DNSNames)+len(x509cr.EmailAddresses)+len(x509cr.IPAddresses) != 0 {
Copy link
Member

Choose a reason for hiding this comment

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

this will not be true for node serving certs

Copy link
Member Author

Choose a reason for hiding this comment

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

Same as above. I will adjust when #45059 settles.

if err != nil {
utilruntime.HandleError(fmt.Errorf("unable to parse csr %q: %v", csr.Name, err))
return csr, nil
}
Copy link
Member

Choose a reason for hiding this comment

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

I expected this to be structured more in terms of recognizers and associated permission required.

type CSRRecognizer interface {
  Matches(csrObj, parsedCSR) bool
}

recognizers := []struct{
  recognizer CSRRecognizer
  permission authorizer.ResourceAttributes
}{
  {
    recognizer: isSelfNodeClientCert,
    permission: authorizer.ResourceAttributes{Group: "certificates.k8s.io", Resource: "certificatesigningrequests", Subresource: "selfnodeclient"},
  },
  {
    recognizer: isNodeClientCert,
    permission: authorizer.ResourceAttributes{Group: "certificates.k8s.io", Resource: "certificatesigningrequests", Subresource: "nodeclient"},
  },
  {
    recognizer: isSelfNodeClientCert,
    permission: authorizer.ResourceAttributes{Group: "certificates.k8s.io", Resource: "certificatesigningrequests", Subresource: "selfnodeclient"},
  },
  {
    recognizer: isNodeClientCert,
    permission: authorizer.ResourceAttributes{Group: "certificates.k8s.io", Resource: "certificatesigningrequests", Subresource: "nodeclient"},
  },
}

func autoApprove(csrObj) {
  // short-circuit on pre-approved
  parsedCSR, err := parse(csrObj)
  // short-circuit on error
  for _, r := range recognizers {
    if !r.recognizer.Matches(csrObj, parsedCSR) {
      continue
    }
    approved, err := authorize(csrObj, r.permission)
    if err != nil {
      // accumulate error in case we ultimately fail?
      continue
    }
    if approved {
      // record approval
    }
  }
}

return csr, nil
}
}
if hasExactUsages(csr, kubeletServerUsages) {
Copy link
Member

@liggitt liggitt May 15, 2017

Choose a reason for hiding this comment

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

have you thought about how we'd verify what hostnames and subjectaltnames a kubelet is allowed to ask for in its serving cert? In non-cloud-provider setups, the only info we have about the node is self-reported today in node status (which isn't even available until the kubelet self-registers, which is probably after the serving cert bootstrap). In cloud-provider cases, are we able to ask the cloud provider for the hostnames/dns names/IP addresses for an arbitrary node?

@ericchiang
Copy link
Contributor

cc @kubernetes/sig-auth-pr-reviews

closes #45030?

@k8s-ci-robot k8s-ci-robot added the sig/auth Categorizes an issue or PR as relevant to SIG Auth. label May 17, 2017
@mikedanese
Copy link
Member Author

@ericchiang yest this closes #45030

if !hasExactUsages(csr, kubeletServerUsages) {
return false
}
//TODO(jcbsmpsn): implement the rest of this
Copy link
Member Author

Choose a reason for hiding this comment

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

@jcbsmpsn I expect you will want to fill this in once we know how kubelet certs are going to look.

@k8s-github-robot k8s-github-robot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels May 23, 2017
@mikedanese mikedanese force-pushed the cert-sar branch 2 times, most recently from b518a7e to d4f7b99 Compare May 23, 2017 22:06
@mikedanese
Copy link
Member Author

@liggitt Please rereview this (especially the second commit)

@k8s-github-robot k8s-github-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label May 29, 2017
@liggitt
Copy link
Member

liggitt commented May 29, 2017

The behavior was alpha and the flag was marked experimental. I'd consider marking the flag as deprecated and making it inert for a release

}

func isNodeClientCert(csr *capi.CertificateSigningRequest, x509cr *x509.CertificateRequest) bool {
if !reflect.DeepEqual([]string{"system:nodes"}, x509cr.Subject.Organization) {
Copy link
Member

Choose a reason for hiding this comment

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

Is there a canonical constant on this anywhere?

if !hasExactUsages(csr, kubeletClientUsages) {
return false
}
if !strings.HasPrefix(x509cr.Subject.CommonName, "system:node:") {
Copy link
Member

Choose a reason for hiding this comment

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

...or this?

Copy link
Member Author

Choose a reason for hiding this comment

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

This existed in the previous code.

if !hasExactUsages(csr, kubeletServerUsages) {
return false
}
//TODO(jcbsmpsn): implement the rest of this
Copy link
Member

Choose a reason for hiding this comment

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

critical for v1.7?

Copy link
Member Author

Choose a reason for hiding this comment

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

No it is not.

@luxas
Copy link
Member

luxas commented May 30, 2017

I'm fine with one release cycle as the deprecation period, so we'll remove this in v1.8 then...

@luxas luxas modified the milestone: v1.7 May 30, 2017
@@ -192,7 +192,6 @@ func (s *CMServer) AddFlags(fs *pflag.FlagSet, allControllers []string, disabled
fs.StringVar(&s.ServiceAccountKeyFile, "service-account-private-key-file", s.ServiceAccountKeyFile, "Filename containing a PEM-encoded private RSA or ECDSA key used to sign service account tokens.")
fs.StringVar(&s.ClusterSigningCertFile, "cluster-signing-cert-file", s.ClusterSigningCertFile, "Filename containing a PEM-encoded X509 CA certificate used to issue cluster-scoped certificates")
fs.StringVar(&s.ClusterSigningKeyFile, "cluster-signing-key-file", s.ClusterSigningKeyFile, "Filename containing a PEM-encoded RSA or ECDSA private key used to sign cluster-scoped certificates")
fs.StringVar(&s.ApproveAllKubeletCSRsForGroup, "insecure-experimental-approve-all-kubelet-csrs-for-group", s.ApproveAllKubeletCSRsForGroup, "The group for which the controller-manager will auto approve all CSRs for kubelet client certificates.")
Copy link
Member

Choose a reason for hiding this comment

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

@mikedanese please re-add the flag, make it inert (it doesn't need to bind to anything), mark it as deprecated and describe the replacement mechanism. We'll remove it completely in 1.8.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

@k8s-github-robot k8s-github-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label May 30, 2017
@mikedanese
Copy link
Member Author

since the permission checks are in the core controller manager, I would sort of expect corresponding single-permission clusterroles to match, so that consumers (like kubeadm) would just need to bind one of those roles to their bootstrapper identity or nodes. @deads2k, would that match your expectations?

This is not neccessary to get in on the first pass.

@liggitt
Copy link
Member

liggitt commented May 30, 2017

nit on short-circuiting CSRs that already have a cert issued in status, LGTM otherwise as a first-pass

@mikedanese
Copy link
Member Author

Ok, will addresss and add lgtm label.

@mikedanese
Copy link
Member Author

mikedanese commented May 30, 2017

There is a bit of good feedback that isn't necessary for 1.7 and that I won't be able to get to before code freeze. I opened #46638 to get back to it.

@mikedanese mikedanese added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label May 30, 2017
@mikedanese
Copy link
Member Author

mikedanese commented May 30, 2017

@k8s-bot pull-kubernetes-unit test this

@k8s-github-robot
Copy link

Automatic merge from submit-queue (batch tested with PRs 46635, 45619, 46637, 45059, 46415)

@k8s-github-robot k8s-github-robot merged commit 4e531f6 into kubernetes:master May 31, 2017
@mikedanese mikedanese deleted the cert-sar branch May 31, 2017 03:18
@liggitt
Copy link
Member

liggitt commented May 31, 2017

thanks, can you open doc PRs against the TLS bootstrapping topic and the authorization topic (to call out the subresources used to authorize)

peebs pushed a commit to coreos/kubernetes that referenced this pull request Jun 6, 2017
Automatic merge from submit-queue (batch tested with PRs 46897, 46899, 46864, 46854, 46875)

kubeadm: Make kubeadm use the right CSR approver for the right version

**What this PR does / why we need it**:

fixes regression caused in: kubernetes#45619

**Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #

Fixes: kubernetes/kubeadm#289

**Special notes for your reviewer**:

cc @pipejakob our e2e CI should probably go green after this change

**Release note**:

```release-note
NONE
```
@mikedanese @pipejakob @timothysc @liggitt
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. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/api-change Categorizes issue or PR as related to adding, removing, or otherwise changing an API lgtm "Looks good to me", indicates that a PR is ready to be merged. release-note-action-required Denotes a PR that introduces potentially breaking changes that require user action. sig/auth Categorizes an issue or PR as relevant to SIG Auth. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants