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 init" doesn't accommodate an external front proxy CA #918

Closed
seh opened this issue Jun 14, 2018 · 17 comments · Fixed by kubernetes/kubernetes#68296
Closed

"kubeadm init" doesn't accommodate an external front proxy CA #918

seh opened this issue Jun 14, 2018 · 17 comments · Fixed by kubernetes/kubernetes#68296
Assignees
Labels
area/security kind/bug Categorizes issue or PR as related to a bug. lifecycle/active Indicates that an issue or PR is actively being worked on by a contributor. priority/important-soon Must be staffed and worked on either currently, or very soon, ideally in time for the next release. sig/cluster-lifecycle Categorizes an issue or PR as relevant to SIG Cluster Lifecycle.
Milestone

Comments

@seh
Copy link

seh commented Jun 14, 2018

What keywords did you search in kubeadm issues before filing this one?

  • certificates
  • certs
  • front-proxy
  • external
  • init

Is this a BUG REPORT or FEATURE REQUEST?

BUG REPORT

Versions

kubeadm version (use kubeadm version):

kubeadm version: &version.Info{Major:"1", Minor:"11+", GitVersion:"v1.11.0-beta.2", GitCommit:"be2cfcf9e44b5162a294e977329d6c8194748c4e", GitTreeState:"clean", BuildDate:"2018-06-07T16:19:15Z", GoVersion:"go1.10.2", Compiler:"gc", Platform:"linux/amd64"}

Environment:

  • Kubernetes version (use kubectl version):
Client Version: version.Info{Major:"1", Minor:"10", GitVersion:"v1.10.4", GitCommit:"5ca598b4ba5abb89bb773071ce452e33fb66339d", GitTreeState:"clean", BuildDate:"2018-06-06T15:22:13Z", GoVersion:"go1.9.6", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"10", GitVersion:"v1.10.4", GitCommit:"5ca598b4ba5abb89bb773071ce452e33fb66339d", GitTreeState:"clean", BuildDate:"2018-06-06T08:00:59Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"linux/amd64"}
  • Cloud provider or hardware configuration:
    AWS EC2
  • OS (e.g. from /etc/os-release):
NAME="Container Linux by CoreOS"
ID=coreos
VERSION=1745.6.0
VERSION_ID=1745.6.0
BUILD_ID=2018-06-08-0926
PRETTY_NAME="Container Linux by CoreOS 1745.6.0 (Rhyolite)"
ANSI_COLOR="38;5;75"
HOME_URL="https://coreos.com/"
BUG_REPORT_URL="https://issues.coreos.com"
COREOS_BOARD="amd64-usr"
  • Kernel (e.g. uname -a):
    Linux ip-10-103-0-214.ec2.internal 4.14.48-coreos-r1 #1 SMP Fri Jun 8 08:51:52 UTC 2018 x86_64 Intel(R) Xeon(R) CPU E5-2676 v3 @ 2.40GHz GenuineIntel GNU/Linux

What happened?

I created all of my private keys and certificates ahead of time, and made them available to kubeadm init as follows:

apiserver-etcd-client.crt
apiserver-etcd-client.key
apiserver-kubelet-client.crt
apiserver-kubelet-client.key
apiserver.crt
apiserver.key
ca.crt
ca.key
etcd-ca.crt
front-proxy-ca.crt
front-proxy-client.crt
front-proxy-client.key
sa.key
sa.pub

Note that the CA key—with file name ca.keyis present, but while there is a front-proxy-ca.crt file with the front proxy CA's certificate, the corresponding private key is not present. Even though I am acting as an "external CA" here, I supplied the ca.key file to kubeadm init in order to allow it to generate the various KUBECONFIG files, which are onerous to generate "by hand." However, kubeadm init has no need for my front proxy CA private key here, as I've already generated the client certificate and its private key.

I run kubeadm init, and it fails because it can't find the front-proxy-ca.crt file.

$ sudo kubeadm --v=8 --config "/etc/kubernetes/kubeadm/master-config.yaml" init
I0613 20:36:10.849250   11195 masterconfig.go:113] loading configuration from the given file
I0613 20:36:11.282229   11195 feature_gate.go:230] feature gates: &{map[]}
I0613 20:36:11.282269   11195 init.go:250] [init] validating feature gates
[init] using Kubernetes version: v1.10.4
[preflight] running pre-flight checks
I0613 20:36:11.282315   11195 checks.go:587] validating kubernetes and kubeadm version
I0613 20:36:11.282336   11195 checks.go:182] validating if the firewall is enabled and active
I0613 20:36:11.301093   11195 checks.go:219] validating availability of port 6443
I0613 20:36:11.301351   11195 checks.go:219] validating availability of port 10251
I0613 20:36:11.301479   11195 checks.go:219] validating availability of port 10252
I0613 20:36:11.301600   11195 checks.go:294] validating the existence of file /etc/kubernetes/manifests/kube-apiserver.yaml
I0613 20:36:11.301744   11195 checks.go:294] validating the existence of file /etc/kubernetes/manifests/kube-controller-manager.yaml
I0613 20:36:11.301879   11195 checks.go:294] validating the existence of file /etc/kubernetes/manifests/kube-scheduler.yaml
I0613 20:36:11.301956   11195 checks.go:294] validating the existence of file /etc/kubernetes/manifests/etcd.yaml
I0613 20:36:11.302095   11195 checks.go:444] validating if the connectivity type is via proxy or direct
I0613 20:36:11.302210   11195 checks.go:480] validating http connectivity to first IP address in the CIDR
I0613 20:36:11.302342   11195 checks.go:480] validating http connectivity to first IP address in the CIDR
I0613 20:36:11.302473   11195 checks.go:141] validating if the service is enabled and active
I0613 20:36:11.328251   11195 checks.go:343] validating the contents of file /proc/sys/net/bridge/bridge-nf-call-iptables
I0613 20:36:11.328413   11195 checks.go:343] validating the contents of file /proc/sys/net/ipv4/ip_forward
I0613 20:36:11.328547   11195 checks.go:659] validating whether swap is enabled or not
I0613 20:36:11.328662   11195 checks.go:384] validating the presence of executable ip
I0613 20:36:11.328779   11195 checks.go:384] validating the presence of executable iptables
I0613 20:36:11.328889   11195 checks.go:384] validating the presence of executable mount
I0613 20:36:11.328973   11195 checks.go:384] validating the presence of executable nsenter
I0613 20:36:11.329046   11195 checks.go:384] validating the presence of executable ebtables
I0613 20:36:11.329129   11195 checks.go:384] validating the presence of executable ethtool
I0613 20:36:11.329188   11195 checks.go:384] validating the presence of executable socat
I0613 20:36:11.329266   11195 checks.go:384] validating the presence of executable tc
I0613 20:36:11.329288   11195 checks.go:384] validating the presence of executable touch
I0613 20:36:11.329361   11195 checks.go:529] running all checks
I0613 20:36:11.330093   11195 kernel_validator.go:81] Validating kernel version
I0613 20:36:11.330165   11195 kernel_validator.go:96] Validating kernel config
	[WARNING SystemVerification]: docker version is greater than the most recently validated version. Docker version: 18.03.1-ce. Max validated version: 17.03
I0613 20:36:11.344886   11195 checks.go:414] validating if hostname match dns sub domain
I0613 20:36:11.371318   11195 checks.go:628] validating kubelet version
I0613 20:36:11.441685   11195 checks.go:141] validating if the service is enabled and active
I0613 20:36:11.461287   11195 checks.go:219] validating availability of port 10250
I0613 20:36:11.461359   11195 checks.go:318] validating the existence of file /etc/kubernetes/pki/etcd-ca.crt
I0613 20:36:11.461381   11195 checks.go:318] validating the existence of file /etc/kubernetes/pki/apiserver-etcd-client.crt
I0613 20:36:11.461400   11195 checks.go:318] validating the existence of file /etc/kubernetes/pki/apiserver-etcd-client.key
I0613 20:36:11.461417   11195 checks.go:700] validating the external etcd version
I0613 20:36:11.875581   11195 init.go:281] [init] Getting certificates directory from configuration
I0613 20:36:11.875617   11195 init.go:290] Stopping the kubelet
I0613 20:36:11.994708   11195 flags.go:102] setting kubelet hostname-override to %qip-10-103-0-142.ec2.internal
[kubelet] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
I0613 20:36:11.996284   11195 init.go:306] Starting the kubelet
[preflight] Activating the kubelet service
I0613 20:36:12.149856   11195 init.go:317] [init] creating PKI Assets
I0613 20:36:12.149881   11195 certs.go:37] creating PKI assets
I0613 20:36:12.149891   11195 certs.go:73] create a new self signed cluster CA certificate and key files
[certificates] Using the existing ca certificate and key.
I0613 20:36:12.442226   11195 certs.go:91] creating a new certificate and key files for the apiserver
[certificates] Using the existing apiserver certificate and key.
I0613 20:36:12.610053   11195 certs.go:115] creating a new certificate for kubelets calling apiserver
[certificates] Using the existing apiserver-kubelet-client certificate and key.
I0613 20:36:12.918905   11195 certs.go:253] creating a new public/private key files for signing service account users
[certificates] Using the existing sa key.
I0613 20:36:13.012077   11195 certs.go:272] creating a self signed front proxy CA certificate and key files
failure loading front-proxy-ca certificate: couldn't load the private key file /etc/kubernetes/pki/front-proxy-ca.key: open /etc/kubernetes/pki/front-proxy-ca.key: no such file or directory

What you expected to happen?

kubeadm init should recognize that I am operating an "external front proxy CA" here, and do as it does for etcd and the other master-related keys and certificates: confirm that the key and certificate present are valid and signed by the provided CA certificate, but otherwise leave the files alone and not generate any files to replace them.

How to reproduce it (as minimally and precisely as possible)?

Unfortunately, this is easier said than done, but I'll lay out the steps here:

  • Generate all the private keys and certificates as prescribed by the kubeadm documentation.
  • Include the ca.key file, but omit the front-proxy-ca.crt file.
  • On one of the master machines with those files present, run kubeadm init.
  • Observe that while kubeadm concludes that it is not operating in "external CA mode"—because the ca.key file is present—it does not conclude that it should respect the same contract for the four files related to the front proxy:
    • front-proxy-client.crt
    • front-proxy-client.key
    • front-proxy-ca.crt
    • front-proxy-ca.key (absent)

Anything else we need to know?

We touched on this topic before with #752 and kubernetes/kubernetes#62643, but the latter patch did not fix this problem, because it was addressing the case where we're operating in "external CA mode."

What's missing from kubeadm for now is the notion of an "external front proxy CA mode." We shouldn't roll handling of the front proxy-related PKI assets together with the rest of the control plane PKI assets, because they have separate CAs. It's possible that one CA is external while another is not.

Handling this with separate sub-phases would clarify what's going on. We could determine whether we're operating in "external control plane CA mode," and if not, generate the following files:

  • ca.key
  • ca.crt
  • apiserver.key
  • apiserver.crt
  • apiserver-kubelet-client.key
  • apiserver-kubelet-client.crt

Next, determine whether we're operating in "external front proxy CA mode," and if not, generate the following files:

  • front-proxy-ca.key
  • front-proxy-ca.crt
  • front-proxy-client.key
  • front-proxy-client.crt

It should be possible to have kubeadm step in for CA management for either or both of these sets of control plane and front proxy files.

Without this tolerance in place, I can't use kubeadm init, and I instead have to drop down to the kubeadm alpha phase subcommands, mimicking everything kubeadm init does except the certificate generation phase. That's a brittle approach, given the lack of stability threatened by the very name of the subcommand: "alpha."

Peripherally related, but for etcd: #807 and kubernetes/kubernetes#63806, so perhaps @detiber has thought through this problem before.

@detiber
Copy link
Member

detiber commented Jun 14, 2018

/kind feature
/priority important-soon
/assign

@k8s-ci-robot k8s-ci-robot added kind/feature Categorizes issue or PR as related to a new feature. priority/important-soon Must be staffed and worked on either currently, or very soon, ideally in time for the next release. labels Jun 14, 2018
@detiber
Copy link
Member

detiber commented Jun 14, 2018

/area security
/sig cluster-lifecycle

@k8s-ci-robot k8s-ci-robot added area/security sig/cluster-lifecycle Categorizes an issue or PR as relevant to SIG Cluster Lifecycle. labels Jun 14, 2018
@detiber detiber added this to the v1.12 milestone Jun 14, 2018
@timothysc
Copy link
Member

/assign @liztio

@timothysc timothysc added kind/bug Categorizes issue or PR as related to a bug. and removed kind/feature Categorizes issue or PR as related to a new feature. labels Aug 21, 2018
@liztio liztio added the lifecycle/active Indicates that an issue or PR is actively being worked on by a contributor. label Sep 4, 2018
@liztio
Copy link

liztio commented Sep 4, 2018

Verified. Quick and easy repro:

kubeadm init
cp -r /etc/kubernetes/pki ~
kubeadm reset
cp -r ~/pki/* /etc/kubernetes/pki
rm /etc/kubernetes/pki/front-proxy-ca.key
kubeadm init

gyuho pushed a commit to gyuho/kubernetes that referenced this issue Sep 6, 2018
…certs

Automatic merge from submit-queue (batch tested with PRs 68087, 68256, 64621, 68299, 68296). If you want to cherry-pick this change to another branch, please follow the instructions here: https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md.

Fixes using externally managed certs for kubeadm

**What this PR does / why we need it**:
The certificates overhaul caused a regression when using external certificates. This fixes that issue so external CAs no longer require a key if all certificates exist.

Walk the certificate tree, at each step checking for a CACert.
If the CACert is found, try to use it to generate certificates.
Otherwise, generate a new CA cert.

**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*:
Fixes kubernetes/kubeadm#918

**Special notes for your reviewer**:

**Release note**:

```release-note
External CAs can now be used for kubeadm with only a certificate, as long as all required certificates already exist.
```
@seh
Copy link
Author

seh commented Sep 7, 2018

Thank you, @liztio! I look forward to this in the next release.

@seh
Copy link
Author

seh commented Nov 15, 2018

Trying this with version "v1.13-beta.0," I see the following:

[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Using the existing "ca" certificate and key
[certs] Using the existing "apiserver-kubelet-client" certificate and key
[certs] Using the existing "apiserver" certificate and key
error execution phase certs/front-proxy-ca: failure loading front-proxy-ca certificate: couldn't load the private key file /etc/kubernetes/pki/front-proxy-ca.key: open /etc/kubernetes/pki/front-proxy-ca.key: no such file or directory

Here are the set of files:

# ls -l /etc/kubernetes/pki
total 52
-rw-r--r--. 1 root root 1253 Nov 15 18:44 apiserver-etcd-client.crt
lrwxrwxrwx. 1 root root   33 Nov 15 18:44 apiserver-etcd-client.key -> /etc/kubernetes/pki/apiserver.key
-rw-r--r--. 1 root root 1533 Nov 15 18:44 apiserver-kubelet-client.crt
-rw-------. 1 root root 1675 Nov 15 18:44 apiserver-kubelet-client.key
-rw-r--r--. 1 root root 1830 Nov 15 18:44 apiserver.crt
-rw-------. 1 root root 1675 Nov 15 18:44 apiserver.key
-rw-r--r--. 1 root root 1472 Nov 15 18:44 ca.crt
-rw-------. 1 root root 1679 Nov 15 18:44 ca.key
-rw-r--r--. 1 root root  928 Nov 15 18:44 etcd-ca.crt
-rw-r--r--. 1 root root 1484 Nov 15 18:44 front-proxy-ca.crt
-rw-r--r--. 1 root root 1513 Nov 15 18:44 front-proxy-client.crt
-rw-------. 1 root root 1679 Nov 15 18:44 front-proxy-client.key
-rw-------. 1 root root 1679 Nov 15 18:44 sa.key
-rw-r--r--. 1 root root  451 Nov 15 18:44 sa.pub

Note that the front-proxy-ca.crt file is there, and the certificate in front-proxy-client.crt has its companion key in front-proxy-client.key.

@seh
Copy link
Author

seh commented Nov 16, 2018

Here we can see that the client certificate is signed by the CA, and that the private key matches the certificate:

# openssl verify -verbose -CAfile front-proxy-ca.crt front-proxy-client.crt 
front-proxy-client.crt: OK

# openssl x509 -noout -modulus -in front-proxy-client.crt | openssl md5
(stdin)= 2b733f26443552cded2048fc33db06f5

# openssl rsa -noout -modulus -in front-proxy-client.key | openssl md5
(stdin)= 2b733f26443552cded2048fc33db06f5

What else would you suggest that I investigate here?

@seh
Copy link
Author

seh commented Nov 16, 2018

Pertinent discussion in the "kubeadm" channel of the "Kubernetes" Slack team:

@seh
Copy link
Author

seh commented Nov 23, 2018

Trying release v1.13.0-beta.2 this morning, I see this new problem, which may be related to the reworking of the certificate phases:

[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Using existing front-proxy-ca certificate authority
[certs] Using existing front-proxy-client certificate and key on disk
[certs] External etcd mode: Skipping etcd/ca certificate authority generation
[certs] External etcd mode: Skipping etcd/peer certificate authority generation
[certs] External etcd mode: Skipping etcd/healthcheck-client certificate authority generation
error execution phase certs/apiserver-etcd-client: couldn't load CA certificate etcd-ca: couldn't load the certificate file /etc/kubernetes/pki/etcd/ca.crt: open /etc/kubernetes
/pki/etcd/ca.crt: no such file or directory

I've never needed the etcd CA certificate at that location before, given that my etcd is hosted externally. The API server etcd client files are there, together with the etcd CA certificate in file etcd-ca.crt, as always:

# ls -l /etc/kubernetes/pki/
total 68
-rw-r--r--. 1 root root 1253 Nov 23 13:57 apiserver-etcd-client.crt
lrwxrwxrwx. 1 root root   33 Nov 23 13:57 apiserver-etcd-client.key -> /etc/kubernetes/pki/apiserver.key
-rw-r--r--. 1 root root 1533 Nov 23 13:57 apiserver-kubelet-client.crt
-rw-------. 1 root root 1675 Nov 23 13:57 apiserver-kubelet-client.key
-rw-r--r--. 1 root root 1830 Nov 23 13:57 apiserver.crt
-rw-------. 1 root root 1679 Nov 23 13:57 apiserver.key
-rw-r--r--. 1 root root 1472 Nov 23 13:57 ca.crt
-rw-------. 1 root root 1679 Nov 23 13:57 ca.key
-rw-r--r--. 1 root root  928 Nov 23 13:57 etcd-ca.crt
-rw-r--r--. 1 root root 1484 Nov 23 13:57 front-proxy-ca.crt
-rw-r--r--. 1 root root 1505 Nov 23 13:57 front-proxy-client.crt
-rw-------. 1 root root 1675 Nov 23 13:57 front-proxy-client.key
-rw-------. 1 root root 1675 Nov 23 13:57 sa.key
-rw-r--r--. 1 root root  451 Nov 23 13:57 sa.pub

If I copy the etcd-ca.crt file to etcd/ca.crt, then kubeadm init succeeds. If I move that file, so that there's no etcd-ca.crt file in place, kubeadm init fails its preflight check:

[init] Using Kubernetes version: v1.13.0-beta.2
[preflight] Running pre-flight checks
error execution phase preflight: [preflight] Some fatal errors occurred:
        [ERROR ExternalEtcdClientCertificates]: /etc/kubernetes/pki/etcd-ca.crt doesn't exist
        [ERROR ExternalEtcdVersion]: couldn't load external etcd's server certificate /etc/kubernetes/pki/etcd-ca.crt:  open /etc/kubernetes/pki/etcd-ca.crt: no such file or directory
[preflight] If you know what you are doing, you can make a check non-fatal with `--ignore-preflight-errors=...`

@liztio, this second copy of the etcd CA certificate shouldn't be required, right?

@seh
Copy link
Author

seh commented Nov 23, 2018

Oh, I see what's going on: kubeadm init now wants to find the etcd CA certificate file at etcd/ca.crt, but in my ClusterConfiguration file supplied to kubeadm init, the "etcd.external.caFile" field still mentions the previous etcd-ca.crt file. It looks like I have to migrate both of these together. I'll try that now and report back on how I fare.

@seh
Copy link
Author

seh commented Nov 23, 2018

Well, fixing up that path now leads to this problem:

error execution phase preflight: [preflight] Some fatal errors occurred:
        [ERROR ExternalEtcdVersion]: Get https://etcd0.000-003.kubernetes.local:2379/version: x509: certificate signed by unknown authority
        [ERROR ExternalEtcdVersion]: Get https://etcd1.000-003.kubernetes.local:2379/version: x509: certificate signed by unknown authority
        [ERROR ExternalEtcdVersion]: Get https://etcd2.000-003.kubernetes.local:2379/version: x509: certificate signed by unknown authority
[preflight] If you know what you are doing, you can make a check non-fatal with `--ignore-preflight-errors=...`

I confirmed that the etcd CA certificate file on this master matches the CA certificate on all the etcd servers, and that the API server etcd client certificate is indeed signed with this CA certificate. Using these files with etcdctl works just fine.

@seh
Copy link
Author

seh commented Nov 23, 2018

Sorry for the noise; I realized that my configuration change didn't make it through properly, and I had mistakenly set the external etcd CA certificate path to that of the master's CA certificate file. The right files are in place, but my configuration file is still messed up.

@neolit123 neolit123 removed this from the v1.12 milestone Nov 24, 2018
@neolit123 neolit123 added this to the v1.14 milestone Nov 24, 2018
@seh
Copy link
Author

seh commented Nov 24, 2018

I think I see the problem here. In cmd/kubeadm/app/cmd/phases/certs.go's newCertSubPhases function, we use the default certificate set, whether or not etcd is external or local. That default set includes an entry for the API server etcd client triple (CA certificate, client key, and client certificate), and that entry (KubeadmCertEtcdAPIClient) nominates a CA certificate that has a hard-coded basename path, which is defined as etcd/ca.

If we're using an externally hosted etcd, then when we go looking for its CA certificate, we should honor the configuration's ExternalEtcd.CAFile field. Doing that is not so simple, given the current data-driven approach in this procedure.

Perhaps the KubeadmCert type could introduce a "CANameFunc" field of type func(*kubeadmapi.InitConfiguration) (string) (or func(*kubeadmapi.InitConfiguration) (string, error)) that could return a path predicated on whether etcd is hosted locally or externally. Alternately, we could use some interface that delegates to KubeadmCert, but has an alternate implementation that uses an etcd CA certificate path read from configuration.

@seh
Copy link
Author

seh commented Nov 24, 2018

I filed a fresh issue: #1276.

@fabriziopandini
Copy link
Member

@seh
If I get this thread right the problem on front-proxy CA is fixed, but there is another issue related to etcd names in case of external ca.
If so, given #1276, this issue can closed, right?

@seh
Copy link
Author

seh commented Nov 25, 2018

Yes, we can close this one. Thank you again for the fix.

@spiffxp
Copy link
Member

spiffxp commented Feb 20, 2019

/milestone clear

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/security kind/bug Categorizes issue or PR as related to a bug. lifecycle/active Indicates that an issue or PR is actively being worked on by a contributor. priority/important-soon Must be staffed and worked on either currently, or very soon, ideally in time for the next release. sig/cluster-lifecycle Categorizes an issue or PR as relevant to SIG Cluster Lifecycle.
Projects
None yet
8 participants