-
Notifications
You must be signed in to change notification settings - Fork 40k
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
Conversation
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 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. |
/ok-to-test |
There was a problem hiding this 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!
cmd/kubeadm/app/cmd/init.go
Outdated
} | ||
|
||
} else { | ||
fmt.Printf("[externalca] No ca.key detected, assuming external CA. Skipping certs and kubeconfig.\n") |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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"] = "" |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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") { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
From the verify job:
Please fix. |
This definitely deserves a release note 😄 |
return true | ||
} | ||
|
||
func ValidateSignedCert(pkiDir string, CABaseName string, baseName string, UXName string) bool { |
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
:+1 |
return false | ||
} | ||
|
||
if !ValidateSignedCert(cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName, kubeadmconstants.APIServerCertAndKeyBaseName, "API server") { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
cmd/kubeadm/app/cmd/init.go
Outdated
if err := cmdphases.CreatePKIAssets(i.cfg); err != nil { | ||
return err | ||
} | ||
if !certphase.UsingExternalCA(i.cfg) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this 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
cmd/kubeadm/app/cmd/init.go
Outdated
return err | ||
} | ||
if res, err := certsphase.UsingExternalCA(i.cfg); !res { | ||
fmt.Printf("[externalca] Not using external CA mode: %v\n", err) |
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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)
cmd/kubeadm/app/cmd/init.go
Outdated
if err := certsphase.CreatePKIAssets(i.cfg); err != nil { | ||
return err | ||
} | ||
if res, err := certsphase.UsingExternalCA(i.cfg); !res { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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 ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
There was a problem hiding this 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() |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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"] = "" |
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 ""
There was a problem hiding this 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"] = "" |
There was a problem hiding this comment.
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 ""
/retest 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.
@luxas @mikedanese I squashed my commits, can I get another lgtm? |
/lgtm |
[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 |
/retest |
/retest Review the full test history for this PR. |
/retest |
/test all [submit-queue is verifying that this PR is safe to merge] |
Automatic merge from submit-queue (batch tested with PRs 50832, 51119, 51636, 48921, 51712) |
@nckturner: The following tests failed, say
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. |
🎉 well done! |
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: