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

Approve Kubelet serving CSR #7

Merged
merged 2 commits into from
Apr 24, 2018
Merged

Conversation

awly
Copy link
Contributor

@awly awly commented Apr 19, 2018

Validate same as client CSRs, plus check DNSNames SAN.

DNSNames should contain GCE VM name. Approver checks it against cluster
project in VM metadata (customer project, not master project).
Optionally it also checks that VM belongs to cluster's NodePool.

@k8s-ci-robot k8s-ci-robot added cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. labels Apr 19, 2018
@awly
Copy link
Contributor Author

awly commented Apr 19, 2018

/assign @mikedanese

@awly
Copy link
Contributor Author

awly commented Apr 19, 2018

It looks like k8s-ci-robot in this repo doesn't run the tests? cc @calebamiles

@mikedanese
Copy link
Member

Can you do the vendor changes in a separate commit?

@awly awly force-pushed the node-server-cert branch 2 times, most recently from e4fd823 to d94a8b5 Compare April 19, 2018 21:38
@awly
Copy link
Contributor Author

awly commented Apr 19, 2018

Remove cluster membership check. Only match csr.Username to CN and check (DNS name + IP address) against GCE API.

Split into vendoring and code commits.

PTAL @mikedanese

return false
}
if csr.Spec.Username != x509cr.Subject.CommonName {
return false
}
return true
}

func validateNodeServerCert(opts approverOptions, csr *capi.CertificateSigningRequest, x509cr *x509.CertificateRequest) bool {
client, err := google.DefaultClient(context.Background())
Copy link
Member

Choose a reason for hiding this comment

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

Does this have a global token source or do we create one per call?

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, this was creating one per call.
Switched to a single shared token source.

for _, iface := range inst.NetworkInterfaces {
for _, ip := range x509cr.IPAddresses {
if ip.String() == iface.NetworkIP {
return nil
Copy link
Member

Choose a reason for hiding this comment

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

why return? don't we meed to validate all ips in the CSR, not just the first?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right.
For some reason I thought "if any dns/ip combo passes it's sufficient". Brain fart.

a.zone = strings.TrimSpace(string(val))

// Get the token source for Application Default Credentials.
a.tokenSource, err = google.DefaultTokenSource(context.Background())
Copy link
Member

Choose a reason for hiding this comment

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

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. Is this for quota or permission reasons?

a.projectID = gceConfig.Global.ProjectID

// Get cluster zone from metadata server.
req, err := http.NewRequest(http.MethodGet, "http://metadata.google.internal/computeMetadata/v1/instance/attributes/cluster-location", nil)
Copy link
Member

Choose a reason for hiding this comment

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

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

validators []csrValidator
}

func newGKEApprover(client clientset.Interface) *gkeApprover {
func newGKEApprover(opts approverOptions, client clientset.Interface) *gkeApprover {
return &gkeApprover{client: client, validators: validators}
Copy link
Member

Choose a reason for hiding this comment

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

save opts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops, done

name: "kubelet server certificate SubjectAccessReview",
recognize: isNodeServerCert,
validate: validateNodeServerCert,
permission: authorization.ResourceAttributes{Group: "certificates.k8s.io", Resource: "certificatesigningrequests", Verb: "create", Subresource: "selfnodeserver"},
Copy link
Member

Choose a reason for hiding this comment

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

We might want to lump this with selfnodeclient. I'm not sure if the separate permission buys us much.

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


outer:
for _, n := range x509cr.DNSNames {
inst, err := compute.NewInstancesService(cs).Get(opts.projectID, opts.zone, n).Do()
Copy link
Member

Choose a reason for hiding this comment

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

Move this out of the loop

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

if len(x509cr.DNSNames) == 0 {
return errors.New("no SAN DNS names")
}
if len(x509cr.IPAddresses) == 0 {
Copy link
Member

@mikedanese mikedanese Apr 23, 2018

Choose a reason for hiding this comment

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

Also consider if len(dnsNames) + len(ipAddrs) == 0 { 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.

Also consider capping the number of SANs at something more than reasonable, e.g. 10 or 20.

Copy link
Member

Choose a reason for hiding this comment

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

Also only allow dns and ip sans.

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

if err != nil {
return a, err
}
a.zone = strings.TrimSpace(zone)
Copy link
Member

Choose a reason for hiding this comment

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

This isn't always going to be a zone. If it's a regional cluster, this will be a region.

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.
Loading list of all zones at startup and matching to location by prefix.
If it's a zone, only one will match.
If it's a region, all zones in it will match via prefix in name.

@awly
Copy link
Contributor Author

awly commented Apr 23, 2018

PTAL @mikedanese

for _, n := range x509cr.DNSNames {
inner:
for _, z := range opts.zones {
inst, err := srv.Get(opts.projectID, z, n).Do()
Copy link
Member

Choose a reason for hiding this comment

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

Can you use the common name to find the instance, and find the instance outside the loop?

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 think we need to loop over zones regardless. The checks above limit how many requests we make.

@awly
Copy link
Contributor Author

awly commented Apr 23, 2018

Changed cluster zones lookup to use VM's region instead of cluster-location.
PTAL

numDNS, numIPs := len(x509cr.DNSNames), len(x509cr.IPAddresses)
switch {
case numDNS != numIPs:
return fmt.Errorf("got %d DNS names and %d IPs, want equal numbers", numDNS, numIPs)
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure this makes sense. There's aren't 1 to 1.

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.
Also changed the check below to disallow len(IPs) > 0 && len(DNS) == 0 (yay unit tests!)

Andrew Lytvynov added 2 commits April 24, 2018 10:48
Validate same as client CSRs, plus check DNSNames/IPAddresses SAN.

DNSNames should contain GCE VM name and IPAddresses should match that
VM's IP. Approver checks it against cluster project in VM metadata
(customer project, not master project).
@awly
Copy link
Contributor Author

awly commented Apr 24, 2018

Squashed all the fixes. PTAL

@mikedanese
Copy link
Member

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Apr 24, 2018
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: mikedanese

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

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Apr 24, 2018
@k8s-ci-robot k8s-ci-robot merged commit ebdac3f into kubernetes:master Apr 24, 2018
@awly awly deleted the node-server-cert branch April 24, 2018 21:24
nrb pushed a commit to nrb/cloud-provider-gcp that referenced this pull request Jul 26, 2023
UPSTREAM: <carry>: GCP CCM fixes
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. lgtm "Looks good to me", indicates that a PR is ready to be merged. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants