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公用而设计的。 至于为什么不行,等有时间再研究。


fzu_huang
293 声望126 粉丝