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

kubeadm: Add support for using an external CA whose key is never stored in the cluster #50832

Merged
merged 1 commit into from
Sep 3, 2017

Conversation

nckturner
Copy link
Contributor

@nckturner nckturner commented Aug 17, 2017

We allow a kubeadm user to use an external CA by checking to see if ca.key is missing and skipping cert checks and kubeconfig generation if ca.key is missing. We also pass an empty arg --cluster-signing-key-file="" to kube controller manager so that the csr signer doesn't start.

What this PR does / why we need it:

This PR allows the kubeadm certs phase and kubeconfig phase to be skipped if the ca.key is missing but all other certs are present.

Which issue this PR fixes :

Fixes kubernetes/kubeadm/issues/280

Special notes for your reviewer:

@luxas @mikedanese @fabriziopandini

Release note:

kubeadm: Add support for using an external CA whose key is never stored in the cluster

@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Aug 17, 2017
@k8s-ci-robot
Copy link
Contributor

Hi @nckturner. Thanks for your PR.

I'm waiting for a kubernetes member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

I understand the commands that are listed here.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.

@k8s-ci-robot k8s-ci-robot added the needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. label Aug 17, 2017
@k8s-github-robot k8s-github-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. release-note-label-needed labels Aug 17, 2017
@dims
Copy link
Member

dims commented Aug 17, 2017

/ok-to-test

@k8s-ci-robot k8s-ci-robot removed the needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. label Aug 17, 2017
Copy link
Member

@luxas luxas left a comment

Choose a reason for hiding this comment

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

Basically LGTM; some asks and nits + unit (which I know you're working on already 👍)

Thanks for this contribution!

}

} else {
fmt.Printf("[externalca] No ca.key detected, assuming external CA. Skipping certs and kubeconfig.\n")
Copy link
Member

Choose a reason for hiding this comment

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

nit: fmt.Println
nit: No ca.key detected, but all other files generated from it are available, so using external CA mode. Won't generate certs nor kubeconfig files (or something like that)

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

"use-service-account-credentials": "true",
"controllers": "*,bootstrapsigner,tokencleaner",
}

// If using external CA, pass empty string to controller manager instead of ca.key path, so that the csrsigning controller fails to start
if certphase.UsingExternalCA(cfg) {
defaultArguments["cluster-signing-key-file"] = ""
Copy link
Member

Choose a reason for hiding this comment

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

for clarity; I'd always set cluster-signing-key-file and cluster-signing-cert-file in defaultArguments
and in this special case; unset both (although one only is technically needed, it's safest to reset both of them

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense, fixed.

return false
}

if !ValidatePrivateKey(cfg.CertificatesDir, kubeadmconstants.ServiceAccountKeyBaseName, "service account") {
Copy link
Member

Choose a reason for hiding this comment

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

What about sa.pub? can we make a case for that one as well?

Copy link
Contributor Author

@nckturner nckturner Aug 21, 2017

Choose a reason for hiding this comment

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

I added a case to superficially check for its existence, tell me what you think.

return true
}

func ValidateCACert(pkiDir string, baseName string, UXName string) bool {
Copy link
Member

Choose a reason for hiding this comment

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

Please add a comment for all these

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

return true
}

func ValidateCACertAndKey(pkiDir string, baseName string, UXName string) bool {
Copy link
Member

Choose a reason for hiding this comment

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

And unit test all these new funcs you are creating.

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, though tell me if I should refactor them into table driven tests.

Copy link
Member

Choose a reason for hiding this comment

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

Table driven tests are common practice, please write such ones.
Nearly all unit tests in cmd/kubeadm are table driven

@luxas
Copy link
Member

luxas commented Aug 17, 2017

From the verify job:

I0817 11:02:41.808] Run ./hack/update-bazel.sh
...
W0817 11:20:11.506] Errors from golint:
W0817 11:20:11.507] cmd/kubeadm/app/phases/certs/certs.go:162:1: exported function ValidateCACert should have comment or be unexported
W0817 11:20:11.510] cmd/kubeadm/app/phases/certs/certs.go:178:1: exported function ValidateCACertAndKey should have comment or be unexported
W0817 11:20:11.510] cmd/kubeadm/app/phases/certs/certs.go:191:1: exported function ValidateSignedCert should have comment or be unexported
W0817 11:20:11.511] cmd/kubeadm/app/phases/certs/certs.go:214:1: exported function ValidatePrivateKey should have comment or be unexported

Please fix.

@luxas luxas changed the title Kubeadm external CA check kubeadm: Add support for using an external CA whose key is never stored in the cluster Aug 17, 2017
@luxas luxas added release-note Denotes a PR that will be considered when it comes time to generate release notes. and removed release-note-label-needed labels Aug 17, 2017
@luxas luxas added this to the v1.8 milestone Aug 17, 2017
@luxas
Copy link
Member

luxas commented Aug 17, 2017

This definitely deserves a release note 😄

return true
}

func ValidateSignedCert(pkiDir string, CABaseName string, baseName string, UXName string) bool {
Copy link
Member

@mikedanese mikedanese Aug 17, 2017

Choose a reason for hiding this comment

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

don't start go local variables with capitals. caBaseName...

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

@fabriziopandini
Copy link
Member

:+1

return false
}

if !ValidateSignedCert(cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName, kubeadmconstants.APIServerCertAndKeyBaseName, "API server") {
Copy link
Member

Choose a reason for hiding this comment

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

make this table driven. https://play.golang.org/p/UJY4McemtD

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can you explain what you are suggesting a little more? Do you mean this entire function?

// Check CA Cert
caCert, err := pkiutil.TryLoadCertFromDisk(pkiDir, baseName)
if err != nil {
fmt.Printf("failure loading certificate for %s: %v", UXName, err)
Copy link
Member

Choose a reason for hiding this comment

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

raw prints without linebreaks are going to look pretty funky in the output.

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

if err := cmdphases.CreatePKIAssets(i.cfg); err != nil {
return err
}
if !certphase.UsingExternalCA(i.cfg) {
Copy link
Member

Choose a reason for hiding this comment

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

According to the issue, it is expected to have also required kubeconfig files on disk. Is kubeadm going to test this precondition?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, that should also be checked, good catch

@k8s-github-robot k8s-github-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 19, 2017
@k8s-github-robot k8s-github-robot added needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. and removed needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. labels Aug 21, 2017
@k8s-github-robot k8s-github-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 29, 2017
Copy link
Member

@luxas luxas left a comment

Choose a reason for hiding this comment

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

Left some nits, please fix at least most of them. Other than that, LGTM

return err
}
if res, err := certsphase.UsingExternalCA(i.cfg); !res {
fmt.Printf("[externalca] Not using external CA mode: %v\n", err)
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we should print anything here, this is the "normal" state anyway...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed the print, if someone wants to see why external CA mode is not active they would have to add print statements to this step. (a logger with verbosity levels would be nice but it seems to be not used in kubeadm)

if err := certsphase.CreatePKIAssets(i.cfg); err != nil {
return err
}
if res, err := certsphase.UsingExternalCA(i.cfg); !res {
Copy link
Member

Choose a reason for hiding this comment

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

err seems to be ignored 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'd say its not an error at this step, so I changed it to be a string representing the reason that we are not in "Using External CA".

}

var p *rsa.PublicKey
p = pubKeys[0].(*rsa.PublicKey)
Copy link
Member

Choose a reason for hiding this comment

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

Any reason to not use := 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.


var key *rsa.PrivateKey
// Allow RSA format only
if k, ok := privKey.(*rsa.PrivateKey); ok {
Copy link
Member

Choose a reason for hiding this comment

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

nit on style, this can be moved out of the if clause and you can check for !ok, return in that case, otherwise just use := afterwards.

Generally I'm trying to minimize the amount of indentation in funcs and avoid if/else when possible. This is still a nit though ;)

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

Copy link
Member

@luxas luxas left a comment

Choose a reason for hiding this comment

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

WDYT about the delete(... comment?

func UsingExternalCA(cfg *kubeadmapi.MasterConfiguration) (bool, string) {

if err := validateCACert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName, "", "CA"}); err != nil {
return false, err.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 actually preferred returning an error like you did in the earlier version, but I'm not blocking this PR on that

return err
}

_, err := pkiutil.TryLoadKeyFromDisk(l.pkiDir, l.caBaseName)
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 for this PR; but in some future PR we should make sure the public key in the cert and key match each other (we have other places that do similar functionality like this so...)

// If using external CA, pass empty string to controller manager instead of ca.key/ca.crt path,
// so that the csrsigning controller fails to start
if res, _ := certphase.UsingExternalCA(cfg); res {
defaultArguments["cluster-signing-key-file"] = ""
Copy link
Member

Choose a reason for hiding this comment

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

Gah, should have spotted this earlier, I realize delete(defaultArguments, "cluster...") is probably preferable over this method... WDYT?

It would be great to get an unit test for this as well if possible...

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 nice thing about setting to "" is that (in controller manager logs) you get:

Skipping "csrsigning"

instead of:

Failed to start certificate controller: error reading CA cert file "/etc/kubernetes/ca/ca.pem": open /etc/kubernetes/ca/ca.pem: no such file or directory

when using delete() which I think is slightly better, but I will switch them if you prefer.

Copy link
Member

Choose a reason for hiding this comment

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

With that context (thanks for reminding me about the UX), it makes sense to pass ""

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Aug 31, 2017
Copy link
Member

@luxas luxas left a comment

Choose a reason for hiding this comment

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

Thanks for this!

/lgtm

// If using external CA, pass empty string to controller manager instead of ca.key/ca.crt path,
// so that the csrsigning controller fails to start
if res, _ := certphase.UsingExternalCA(cfg); res {
defaultArguments["cluster-signing-key-file"] = ""
Copy link
Member

Choose a reason for hiding this comment

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

With that context (thanks for reminding me about the UX), it makes sense to pass ""

@k8s-github-robot k8s-github-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 31, 2017
@fejta-bot
Copy link

/retest
This bot automatically retries jobs that failed/flaked on approved PRs (send feedback to @fejta).

Review the full test history for this PR.

We allow a kubeadm user to use an external CA by checking to see if ca.key is missing and skipping cert checks and kubeconfig generation if ca.key is missing.
@k8s-github-robot k8s-github-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Aug 31, 2017
@nckturner
Copy link
Contributor Author

@luxas @mikedanese I squashed my commits, can I get another lgtm?

@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 Aug 31, 2017
@k8s-github-robot
Copy link

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: luxas, mikedanese, nckturner

Associated issue: 280

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

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

@mikedanese
Copy link
Member

/retest

@fejta-bot
Copy link

/retest
This bot automatically retries jobs that failed/flaked on approved PRs (send feedback to @fejta).

Review the full test history for this PR.

@mikedanese
Copy link
Member

/retest

@k8s-github-robot
Copy link

/test all [submit-queue is verifying that this PR is safe to merge]

@k8s-github-robot
Copy link

Automatic merge from submit-queue (batch tested with PRs 50832, 51119, 51636, 48921, 51712)

@k8s-github-robot k8s-github-robot merged commit 2164f09 into kubernetes:master Sep 3, 2017
@k8s-ci-robot
Copy link
Contributor

@nckturner: The following tests failed, say /retest to rerun them all:

Test name Commit Details Rerun command
pull-kubernetes-e2e-kops-aws e0ab0b5 link /test pull-kubernetes-e2e-kops-aws
pull-kubernetes-e2e-gce-etcd3 e0ab0b5 link /test pull-kubernetes-e2e-gce-etcd3

Full PR test history. Your PR dashboard. Please help us cut down on flakes by linking to an open issue when you hit one in your PR.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.

@fabriziopandini
Copy link
Member

🎉 well done!

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. release-note Denotes a PR that will be considered when it comes time to generate release notes. 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.

Make it easy to run an other certificates controller
8 participants