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

Creating the keyset.yaml file if it does not exist #4582

Merged
merged 2 commits into from
Mar 10, 2018

Conversation

chrislovecnm
Copy link
Contributor

@chrislovecnm chrislovecnm commented Mar 5, 2018

If the Keypair is not found kops creates a new keyset file. We are
setting the Format to v1, which denotes that we do not have a keyset
file.

This commit enables upgrading from kops 1.8 -> 1.9 while upgrading an
existing cluster. Clusters built with kops 1.8 do not have the keyset
file, and these code changes allow the creation of the keyset file.

Fixes: #4546

The only thing I do not like about this fix is that we are generating a new private key. Not a horrible thing, but not sure how stuff like etcd will behave.

Edit: with changes I am not generating a new private key.

@k8s-ci-robot k8s-ci-robot added approved Indicates a PR has been approved by an approver from all required OWNERS files. size/M Denotes a PR that changes 30-99 lines, ignoring generated files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Mar 5, 2018
@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Mar 5, 2018
@chrislovecnm chrislovecnm force-pushed the create-keyset-yaml branch 2 times, most recently from 3a4536f to 3809385 Compare March 5, 2018 22:28
@chrislovecnm
Copy link
Contributor Author

/assign @blakebarnett @mikesplain

@blakebarnett
Copy link

Apparently this results in a new private key being generated for the CA? This would require us to rotate every cert we've created using the CA (a large number), if there's any way to make it re-use the existing private key that would be very preferable.

cc: @justinsb

@chrislovecnm
Copy link
Contributor Author

@blakebarnett I am not certain that a new private cert is being generated or we are creating two private certs with a keyset.yaml.

@justinsb any ideas?

} else {
glog.Warningf("Ignoring changes in key: %v", fi.DebugAsJsonString(changes))
}
}

// TODO: I am wondering if we reuse the certificate information when a.KeysetAPIVersion == "" rather than
// TODO: issuing the new cert
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 am seeing two private pems in the keypair.yaml file. Any idea how we can re-use the pem?

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 that we are eventually calling https://github.com/kubernetes/kops/blob/master/pkg/pki/csr.go#L94

And that is creating the new cert.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

certificateData, err := x509.CreateCertificate(crypto_rand.Reader, template, parent, template.PublicKey, signerPrivateKey.Key)

} else if a.KeypairType == "" {
// The a.KeysetAPIVersion will == "" when the keyset is a legacy keyset and does not have a keypair.yaml
// file store in the state store
createCertificate = true
Copy link
Member

Choose a reason for hiding this comment

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

Why are you setting createCertificate 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.

I can test without it, but I think my testing did not create the keypair.yaml.

Copy link
Member

Choose a reason for hiding this comment

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

As we discussed, pretty sure this shouldn't drive creation of a new certificate, but should instead drive a new UpdateStoredVersion or similar call

pkg/model/pki.go Outdated
@@ -34,6 +34,9 @@ type PKIModelBuilder struct {

var _ fi.ModelBuilder = &PKIModelBuilder{}

// keypairType is used to differentiate the old secretes with the newer versions
Copy link
Member

Choose a reason for hiding this comment

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

typo: secretes

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we need the spelling tool

pkg/model/pki.go Outdated
@@ -34,6 +34,9 @@ type PKIModelBuilder struct {

var _ fi.ModelBuilder = &PKIModelBuilder{}

// keypairType is used to differentiate the old secretes with the newer versions
const keypairType = "Keypair"
Copy link
Member

@justinsb justinsb Mar 6, 2018

Choose a reason for hiding this comment

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

What is the other type? Maybe we have a field "Format" or "APIVersion", and we have "legacy" & "kops/v1alpha2" as that most closely matches what we're actually doing, I think.

Although the obvious problem with this is that I don't know how easy it is to get the stored version from the apimachinery. But I'm OK with cheating at the moment (assuming v1alpha2)

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 was the only thing in the API object that I could find that was actually set. I tested the heck out of this, and the only thing I could figure out how to access is the object name Keypair.

@@ -51,6 +51,9 @@ type Keypair struct {
Subject string `json:"subject"`
// Type the type of certificate i.e. CA, server, client etc
Type string `json:"type"`
// KeypairType stores the api version of kops.Keyset. We are storing this info in order to determine if kops
// is accessing legacy secrets that do not use keyset.yaml.
KeypairType string `json:"keypairType"`
Copy link
Member

Choose a reason for hiding this comment

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

I would call it APIVersion or Format. And then I'd say "This lets us convert the stored keypair to our preferred version." I don't think we should say "We are storing this info" because we're not storing it - tasks are ephemeral

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure ... renaming

@@ -44,7 +44,7 @@ type KeystoreItem struct {
type Keystore interface {
// FindKeypair finds a cert & private key, returning nil where either is not found
// (if the certificate is found but not keypair, that is not an error: only the cert will be returned)
FindKeypair(name string) (*pki.Certificate, *pki.PrivateKey, error)
FindKeypair(name string) (*pki.Certificate, *pki.PrivateKey, string, error)
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 a little worried that we might need a version for both cert & privatekey, but we'll see...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well your test is not working ... see your PR for comments.

} else if a.KeypairType == "" {
// The a.KeysetAPIVersion will == "" when the keyset is a legacy keyset and does not have a keypair.yaml
// file store in the state store
createCertificate = true
Copy link
Member

Choose a reason for hiding this comment

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

As we discussed, pretty sure this shouldn't drive creation of a new certificate, but should instead drive a new UpdateStoredVersion or similar call

}

return keypair.Certificate, keypair.PrivateKey, nil
// TODO: KubernetesKeystore does not seem to be using the newer version of keypairs.
Copy link
Member

Choose a reason for hiding this comment

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

Not sure what you mean. keyset.yaml doesn't make sense here - keyset.yaml makes a filesystem behave more like an API server. So we don't need keyset.yaml for an API-server-backed key store.

@@ -233,27 +233,27 @@ func (c *VFSCAStore) parseKeysetYaml(data []byte) (*kops.Keyset, error) {
// loadCertificatesBundle loads a keyset from the path
// Returns (nil, nil) if the file is not found
// Bundles avoid the need for a list-files permission, which can be tricky on e.g. GCE
func (c *VFSCAStore) loadKeysetBundle(p vfs.Path) (*keyset, error) {
func (c *VFSCAStore) loadKeysetBundle(p vfs.Path) (*keyset, string, error) {
Copy link
Member

Choose a reason for hiding this comment

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

You could put the stored version into the (internal) keyset type - should be simpler

Copy link
Contributor Author

Choose a reason for hiding this comment

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

need more context

// memfs://keystore/private/ca/keyset.yaml
// memfs://keystore/private/kubelet/2.key
// memfs://keystore/private/kubelet/4.key
// memfs://keystore/private/kubelet/keyset.yaml
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Test is failing and this is what I am getting :(

@chrislovecnm chrislovecnm force-pushed the create-keyset-yaml branch 2 times, most recently from f882a0d to 7740ec3 Compare March 9, 2018 18:17
} else if changes.Type != "" {
createCertificate = true
glog.V(8).Infof("creating certificate new Type")
} else {
glog.Warningf("Ignoring changes in key: %v", fi.DebugAsJsonString(changes))
Copy link
Contributor

Choose a reason for hiding this comment

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

@chrislovecnm As per our out-of-band convo, can we suppress this during an upgrade?

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

@robinpercy
Copy link
Contributor

I've run through a couple tests that create a cluster with kops 1.8, then use this patch to upgrade the cluster. keyset.yaml is being created correctly.

@chrislovecnm
Copy link
Contributor Author

/assign @robinpercy

PTAL

@chrislovecnm
Copy link
Contributor Author

/hold

@k8s-ci-robot k8s-ci-robot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Mar 9, 2018
@chrislovecnm chrislovecnm force-pushed the create-keyset-yaml branch 2 times, most recently from c0f1193 to 6939861 Compare March 9, 2018 19:59
Creating the keypair.yaml file if it does not exist.

If the Keypair is not found kops creates a new keyset file.  We are
setting the Keyset Task Format to 'Keypair', which denotes that we do
not have a keypair.yaml file.

This commit enables upgrading from kops 1.8 -> 1.9 while upgrading an
existing cluster.  Clusters built with kops 1.8 do not have the keypair
file, and these code changes allow the creation of that file.
@chrislovecnm
Copy link
Contributor Author

I have refactored some, and added a bunch of code level documentation. Both @robinpercy and I have tested and the upgrades are working well :) When you upgrade to kops 1.9 from kops 1.8 and update and existing cluster built with kops 1.8 the new keypair.yaml files are created.

@robinpercy
Copy link
Contributor

/lgtm

@k8s-ci-robot k8s-ci-robot added lgtm "Looks good to me", indicates that a PR is ready to be merged. and removed lgtm "Looks good to me", indicates that a PR is ready to be merged. labels Mar 9, 2018
@chrislovecnm
Copy link
Contributor Author

@justinsb I patched and modified your test PR here: ae759ce

Added set the Format member, and flipped the boolean value as we talked about. I will drop the commit once you let me know that you have seen the changes.

The pass should be passing now. PTAL

@justinsb
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 Mar 10, 2018
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: chrislovecnm, justinsb, robinpercy

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:
  • OWNERS [chrislovecnm,justinsb]

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

@chrislovecnm
Copy link
Contributor Author

/hold cancel

@k8s-ci-robot k8s-ci-robot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Mar 10, 2018
@k8s-ci-robot k8s-ci-robot merged commit 6606ab4 into kubernetes:master Mar 10, 2018
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/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Upgrade to kops 1.9 alpha 1 - fails with missing CA cert keyset.yaml
6 participants