kube-apiserver启动的时候如果加了如下的参数:
--admission_control=ServiceAccount
会自动生成一个apiserver.crt和apiserver.key文件,所在目录是/var/run/kubernetes/
,并且程序启动后会发现,有一个默认的serviceaccount,这种模式下我们创建的resource都会有一个默认的serviceaccount。
kube-controller-manager启动时有这么两个参数:
--root-ca-file="": If set, this root certificate authority will be included in service account's token secret. This must be a valid PEM-encoded CA bundle.
--service-account-private-key-file="": Filename containing a PEM-encoded private RSA key used to sign service account tokens.
这两个参数指定了要用哪些文件做token和根证书。若我们将这两个参数分别设定值为apiserver.crt和apiserver.key,那么启动后,执行:
kubectl get secrets
命令,并详细地查看该secret可以看到,该secret中有了两个data:ca.crt和token,他们分别是apiserver.crt和apiserver.key进行再次加密后的数据。
这样,我们创建一个heapster,heapster所属的serviceaccount的secret中的两个data,会被copy到创建出来的容器中,我们可以进入该容器找到两个数据。
我们看到kubernetes的源码中,apiserver的模块有这么一个函数,这个函数在启动apiserver的时候会被调用:
func GenerateSelfSignedCert(host, certPath, keyPath string, alternateIPs []net.IP, alternateDNS []string) error {
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return err
}
template := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
CommonName: fmt.Sprintf("%s@%d", host, time.Now().Unix()),
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(time.Hour * 24 * 365),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
...
}
这个函数的功能是创建crt和key,调用这个函数的地方是kubernetes\cmd\kube-apiserver\app\server.go
:
if s.TLSCertFile == "" && s.TLSPrivateKeyFile == "" {
s.TLSCertFile = path.Join(s.CertDirectory, "apiserver.crt")
s.TLSPrivateKeyFile = path.Join(s.CertDirectory, "apiserver.key")
// TODO (cjcullen): Is PublicAddress the right address to sign a cert with?
alternateIPs := []net.IP{config.ServiceReadWriteIP}
alternateDNS := []string{"kubernetes.default.svc", "kubernetes.default", "kubernetes"}
// It would be nice to set a fqdn subject alt name, but only the kubelets know, the apiserver is clueless
// alternateDNS = append(alternateDNS, "kubernetes.default.svc.CLUSTER.DNS.NAME")
if err := util.GenerateSelfSignedCert(config.PublicAddress.String(), s.TLSCertFile, s.TLSPrivateKeyFile, alternateIPs, alternateDNS); err != nil {
glog.Errorf("Unable to generate self signed cert: %v", err)
} else {
glog.Infof("Using self-signed cert (%s, %s)", s.TLSCertFile, s.TLSPrivateKeyFile)
}
}
通过上一篇可以知heapster启动后是要向apiserver做https请求的,所以crt和token必不可少。那为什么我们不直接拿这边的crt和key去用呢?
这篇文章详细地讲了证书生成的相关知识,其中的“添加了SAN的证书生成的过程”和上文源码中调用生成证书的地方是相似的,但是生成证书的过程中,hostname部分引用了了一个host@time.Now()作为/CN。见源代码中这句:
Subject: pkix.Name{
CommonName: fmt.Sprintf("%s@%d", host, time.Now().Unix()),
},
这句将生成crt的时候的hostname设置成了host@time,比如vm-56-65@23542343562 这样的形式,而且每次重启了apiserver,hostname都会变,容器内部可不知道这个hostname,所以根本没法访问。
heapster内部会记录apiserver的几个common name,即kubernetes源码中的:
[]string{"kubernetes.default.svc", "kubernetes.default", "kubernetes"}
我认为这肯定是要与kubernetes自生成的crt公用而设计的。 至于为什么不行,等有时间再研究。