您好,登錄后才能下訂單哦!
本文小編為大家詳細(xì)介紹“kubernetes的證書有哪些”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“kubernetes的證書有哪些”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學(xué)習(xí)新知識(shí)吧。
kubeadm 生成的一坨證書是不是讓人很蒙逼,這些東西沒那么神奇,來深入扒扒其內(nèi)褲。
root@k8s-master:/etc/kubernetes/pki# tree . |-- apiserver.crt |-- apiserver-etcd-client.crt |-- apiserver-etcd-client.key |-- apiserver.key |-- apiserver-kubelet-client.crt |-- apiserver-kubelet-client.key |-- ca.crt |-- ca.key |-- etcd | |-- ca.crt | |-- ca.key | |-- healthcheck-client.crt | |-- healthcheck-client.key | |-- peer.crt | |-- peer.key | |-- server.crt | `-- server.key |-- front-proxy-ca.crt |-- front-proxy-ca.key |-- front-proxy-client.crt |-- front-proxy-client.key |-- sa.key `-- sa.pub 1 directory, 22 files
要深入了解證書的作用,首先需要了解一些原理和具備一些基本知識(shí),比如什么是非對(duì)稱加密,什么是公鑰,私鑰,數(shù)字簽名是啥等。先從RSA算法說起。
非對(duì)稱加密會(huì)生成一個(gè)密鑰對(duì),如上面的sa.key sa.pub就是密鑰對(duì),一個(gè)用于加密一個(gè)用于解密。
明文 + 公鑰 => 密文
密文 + 私鑰 => 明文
那么此時(shí)沒有私鑰,就很難把密文解密。
進(jìn)一步再詳細(xì)看看其原理, 不想關(guān)注的可以跳過下面原理部分:
假設(shè)我們想加密一個(gè)單詞Caesar, 先把它變成一串?dāng)?shù)字,比如Ascii碼 X = 067097101115097114 這也就是我們需要加密的 明碼。 現(xiàn)在來對(duì)X進(jìn)行加密。
找兩個(gè)很大的質(zhì)數(shù) P 和 Q 計(jì)算他們的乘積 N = P * Q 再令M = (P - 1)(Q - 1)
找到一個(gè)數(shù)E滿足E和M除了1以外沒有公約數(shù)
找到一個(gè)數(shù)D滿足E乘以D除以M余1, E * D mod M = 1
現(xiàn)在 E就是公鑰,可以公開給任何人進(jìn)行加密
D就是私鑰,用于解密,一定要自己保存好
聯(lián)系公鑰和私鑰的N是公開的, 為什么這個(gè)可以公開,就是因?yàn)楦鶕?jù)P Q算出N很簡單,但是把N分解成P Q兩個(gè)大質(zhì)數(shù)非常的難,所以公開了現(xiàn)有的計(jì)算機(jī)算力也很難破解
現(xiàn)在來加密:
pow(X,E) mod N = Y Y就是密文,現(xiàn)在沒有D(私鑰) 神仙也沒法算出X(明文)
解密:
pow(Y,D) mod N = X X是明文,明文就出來了。
數(shù)學(xué)是不是很神奇,現(xiàn)在可認(rèn)為 sa.key = D sa.pub = E
假設(shè)你寫一封信給老板,內(nèi)容是"老板我崇拜你",然后讓同事把信送給老板,怎么確定這信就是你寫的,而且怎么防止同事送信過程中把信改成 "老板你是個(gè)SB"?
可以這樣做,首先你生成一個(gè)密鑰對(duì),把公鑰給老板,然后對(duì)信的內(nèi)容做一個(gè)hash摘要,再用私鑰對(duì)摘要進(jìn)行加密,結(jié)果就是簽名
這樣老板拿到信之后用公鑰進(jìn)行解密,發(fā)現(xiàn)得到的hash值與信的hash值是一致的,這樣確定了信就是你寫的
所以數(shù)字簽名是加密技術(shù)的一種運(yùn)用,與完全加密信息的區(qū)別是這里信息是公開的,你的同事可以看到你吹捧老板。
通常我們配置https服務(wù)時(shí)需要到"權(quán)威機(jī)構(gòu)"申請(qǐng)證書。
過程是這樣的:
網(wǎng)站創(chuàng)建一個(gè)密鑰對(duì),提供公鑰和組織以及個(gè)人信息給權(quán)威機(jī)構(gòu)
權(quán)威機(jī)構(gòu)頒發(fā)證書
瀏覽網(wǎng)頁的朋友利用權(quán)威機(jī)構(gòu)的根證書公鑰解密簽名,對(duì)比摘要,確定合法性
客戶端驗(yàn)證域名信息有效時(shí)間等(瀏覽器基本都內(nèi)置各大權(quán)威機(jī)構(gòu)的CA公鑰)
這個(gè)證書包含如下內(nèi)容:
申請(qǐng)者公鑰
申請(qǐng)者組織和個(gè)人信息
簽發(fā)機(jī)構(gòu)CA信息,有效時(shí)間,序列號(hào)等
以上信息的簽名
根證書又名自簽名證書,也就是自己給自己頒發(fā)的證書。CA(Certificate Authority)被稱為證書授權(quán)中心,k8s中的ca證書就是根證書。
有了以上基礎(chǔ),下面咱們正式開始。。。
先分類:
密鑰對(duì):sa.key sa.pub 根證書:ca.crt etcd/ca 私鑰 : ca.key 等 其它證書
首先其它證書都是由CA根證書頒發(fā)的,kubernetes與etcd使用了不同的CA, 很重要的一點(diǎn)是證書是用于客戶端校驗(yàn)還是服務(wù)端校驗(yàn)。 下面一個(gè)一個(gè)來看:
提供給 kube-controller-manager 使用. kube-controller-manager 通過 sa.key 對(duì) token 進(jìn)行簽名, master 節(jié)點(diǎn)通過公鑰 sa.pub 進(jìn)行簽名的驗(yàn)證 如 kube-proxy 是以 pod 形式運(yùn)行的, 在 pod 中, 直接使用 service account 與 kube-apiserver 進(jìn)行認(rèn)證, 此時(shí)就不需要再單獨(dú)為 kube-proxy 創(chuàng)建證書了, 會(huì)直接使用token校驗(yàn)
根證書
pki/ca.crt pki/ca.key
為k8s集群證書簽發(fā)機(jī)構(gòu)
apiserver 證書
pki/apiserver.crt pki/apiserver.key
kubelet證書
pki/apiserver-kubelet-client.crt pki/apiserver-kubelet-client.key
kubelet要主動(dòng)訪問kube-apiserver, kube-apiserver也需要主動(dòng)向kubelet發(fā)起請(qǐng)求, 所以雙方都需要有自己的根證書以及使用該根證書簽發(fā)的服務(wù)端證書和客戶端證書. 在kube-apiserver中, 一般明確指定用于https訪問的服務(wù)端證書和帶有CN用戶名信息的客戶端證書. 而在kubelet的啟動(dòng)配置中, 一般只指定了ca根證書, 而沒有明確指定用于https訪問的服務(wù)端證書,在生成服務(wù)端證書時(shí), 一般會(huì)指定服務(wù)端地址或主機(jī)名, kube-apiserver相對(duì)變化不是很頻繁, 所以在創(chuàng)建集群之初就可以預(yù)先分配好用作 kube-apiserver的IP 或主機(jī)名/域名, 但是由于部署在node節(jié)點(diǎn)上的kubelet會(huì)因?yàn)榧阂?guī)模的變化而頻繁變化, 而無法預(yù)知node的所有IP信息, 所以kubelet上一般不會(huì)明確指定服務(wù)端證書, 而是只指定ca根證書, 讓kubelet根據(jù)本地主機(jī)信息自動(dòng)生成服務(wù)端證書并保存到配置的cert-dir文件夾中
Aggregation 證書
代理根證書:
pki/front-proxy-ca.crt pki/front-proxy-ca.key
由代理根證書簽發(fā)的客戶端證書:
pki/front-proxy-client.crt pki/front-proxy-client.key
比如使用kubectl proxy代理訪問時(shí),kube-apiserver使用這個(gè)證書來驗(yàn)證客戶端證書是否是自己簽發(fā)的證書。
etcd 根證書
pki/etcd/ca.crt pki/etcd/ca.key
etcd節(jié)點(diǎn)間相互通信 peer證書
由根證書簽發(fā)
pki/etcd/peer.crt pki/etcd/peer.key
pod中Liveness探針客戶端證書
pki/etcd/healthcheck-client.crt pki/etcd/healthcheck-client.key
可查看yaml探活配置:
Liveness: exec [/bin/sh -ec ETCDCTL_API=3 etcdctl \ --endpoints=https://[127.0.0.1]:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt \ --key=/etc/kubernetes/pki/etcd/healthcheck-client.key get foo] \ delay=15s timeout=15s period=10s #success=1 #failure=8
apiserver訪問etcd的證書
pki/apiserver-etcd-client.crt pki/apiserver-etcd-client.key
這里注意一下客戶端證書與服務(wù)端證書區(qū)別,服務(wù)端證書通常會(huì)校驗(yàn)地址域名等。
kubeadm把證書時(shí)間寫死成了1年(client-go就寫死了),這是個(gè)悲傷的故事,導(dǎo)致sealos不得不把證書生成的邏輯剝離出來以讓安裝支持任意過期時(shí)間。
下面根據(jù)源碼來深入體驗(yàn)下kubeadm的證書生成,直接看kubeadm代碼可能有點(diǎn)累,sealos/cert目錄剝離出核心的代碼更容易讀懂一些。
以下為了突出核心邏輯,代碼中刪除一些錯(cuò)誤處理細(xì)節(jié),有興趣可閱讀github.com/fanux/sealos/cert源碼
// create sa.key sa.pub for service Account func GenerateServiceAccountKeyPaire(dir string) error { key, err := NewPrivateKey(x509.RSA) pub := key.Public() err = WriteKey(dir, "sa", key) return WritePublicKey(dir, "sa", pub) }
生成私鑰, 這里的keyType是x509.RSA
func NewPrivateKey(keyType x509.PublicKeyAlgorithm) (crypto.Signer, error) { if keyType == x509.ECDSA { return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) } return rsa.GenerateKey(rand.Reader, rsaKeySize) }
會(huì)返回ca.crt(自簽名證書) ca.key(私鑰)
func NewCaCertAndKey(cfg Config) (*x509.Certificate, crypto.Signer, error) { key, err := NewPrivateKey(x509.UnknownPublicKeyAlgorithm) cert, err := NewSelfSignedCACert(key, cfg.CommonName, cfg.Organization, cfg.Year) return cert, key, nil }
根據(jù)私鑰生成自簽名證書, NotAfter就是證書過期時(shí)間,我們很友好的加了個(gè)變量而不是寫死:
// NewSelfSignedCACert creates a CA certificate func NewSelfSignedCACert(key crypto.Signer, commonName string, organization []string, year time.Duration) (*x509.Certificate, error) { now := time.Now() tmpl := x509.Certificate{ SerialNumber: new(big.Int).SetInt64(0), Subject: pkix.Name{ CommonName: commonName, Organization: organization, }, NotBefore: now.UTC(), NotAfter: now.Add(duration365d * year).UTC(), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, BasicConstraintsValid: true, IsCA: true, } certDERBytes, err := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, key.Public(), key) return x509.ParseCertificate(certDERBytes) }
非常要注意里面的CommonName和Organization字段,非常有用,比如我們創(chuàng)建一個(gè)k8s用戶指定該用戶屬于哪個(gè)用戶組,對(duì)應(yīng)上面這兩個(gè)字段。
比如證書中 fanux 屬于 sealyun這個(gè)組織,那么生成一個(gè)kubeconfig, 就相當(dāng)于有了fanux這個(gè)用戶,這樣k8s在做認(rèn)證時(shí)只需要校驗(yàn)簽名就行,而不需要去訪問 數(shù)據(jù)庫來做認(rèn)證,這非常有利于apiserver的橫向擴(kuò)展。
密鑰對(duì)還是自己生成,然后簽證書時(shí)會(huì)把根證書信息帶上
func NewCaCertAndKeyFromRoot(cfg Config, caCert *x509.Certificate, caKey crypto.Signer) (*x509.Certificate, crypto.Signer, error) { key, err := NewPrivateKey(x509.UnknownPublicKeyAlgorithm) cert, err := NewSignedCert(cfg, key, caCert, caKey) return cert, key, nil }
此時(shí)就必須要求有CommonName了,Usages也得指定是服務(wù)端使用還是客戶端使用, 注意與上面SelfSign的區(qū)別
// NewSignedCert creates a signed certificate using the given CA certificate and key func NewSignedCert(cfg Config, key crypto.Signer, caCert *x509.Certificate, caKey crypto.Signer) (*x509.Certificate, error) { serial, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64)) if len(cfg.CommonName) == 0 { return nil, errors.New("must specify a CommonName") } if len(cfg.Usages) == 0 { return nil, errors.New("must specify at least one ExtKeyUsage") } certTmpl := x509.Certificate{ Subject: pkix.Name{ CommonName: cfg.CommonName, Organization: cfg.Organization, }, DNSNames: cfg.AltNames.DNSNames, IPAddresses: cfg.AltNames.IPs, SerialNumber: serial, NotBefore: caCert.NotBefore, NotAfter: time.Now().Add(duration365d * cfg.Year).UTC(), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: cfg.Usages, } certDERBytes, err := x509.CreateCertificate(rand.Reader, &certTmpl, caCert, key.Public(), caKey) return x509.ParseCertificate(certDERBytes) }
根證書列表
var caList = []Config{ { Path: BasePath, BaseName: "ca", CommonName: "kubernetes", Organization: nil, Year: 100, AltNames: AltNames{}, Usages: nil, }, { Path: BasePath, BaseName: "front-proxy-ca", CommonName: "front-proxy-ca", Organization: nil, Year: 100, AltNames: AltNames{}, Usages: nil, }, { Path: EtcdBasePath, BaseName: "ca", CommonName: "etcd-ca", Organization: nil, Year: 100, AltNames: AltNames{}, Usages: nil, }, }
其它簽名證書列表
var certList = []Config{ { Path: BasePath, BaseName: "apiserver", CAName: "kubernetes", CommonName: "kube-apiserver", Organization: nil, Year: 100, AltNames: AltNames{// 實(shí)際安裝時(shí)還需要把服務(wù)器IP用戶自定義域名加上 DNSNames: []string{ "apiserver.cluster.local", "localhost", "master", "kubernetes", "kubernetes.default", "kubernetes.default.svc", }, IPs: []net.IP{ {127,0,0,1}, }, }, Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, // 用途是服務(wù)端校驗(yàn) }, { Path: BasePath, BaseName: "apiserver-kubelet-client", CAName: "kubernetes", CommonName: "kube-apiserver-kubelet-client", Organization: []string{"system:masters"}, Year: 100, AltNames: AltNames{}, Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, }, { Path: BasePath, BaseName: "front-proxy-client", CAName: "front-proxy-ca", CommonName: "front-proxy-client", Organization: nil, Year: 100, AltNames: AltNames{}, Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, }, { Path: BasePath, BaseName: "apiserver-etcd-client", CAName: "etcd-ca", CommonName: "kube-apiserver-etcd-client", Organization: []string{"system:masters"}, Year: 100, AltNames: AltNames{}, Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, }, { Path: EtcdBasePath, BaseName: "server", CAName: "etcd-ca", CommonName: "etcd", // kubeadm etcd server證書common name使用節(jié)點(diǎn)名,這也是調(diào)用時(shí)需要改動(dòng)的 Organization: nil, Year: 100, AltNames: AltNames{}, // 調(diào)用時(shí)需要把節(jié)點(diǎn)名,節(jié)點(diǎn)IP等加上 Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, }, { Path: EtcdBasePath, BaseName: "peer", CAName: "etcd-ca", CommonName: "etcd-peer", // 與etcd server同理 Organization: nil, Year: 100, AltNames: AltNames{}, // 與etcd server同理 Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, }, { Path: EtcdBasePath, BaseName: "healthcheck-client", CAName: "etcd-ca", CommonName: "kube-etcd-healthcheck-client", Organization: []string{"system:masters"}, Year: 100, AltNames: AltNames{}, Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, }, }
上面非常要注意的是server端校驗(yàn)的證書安裝時(shí)需要把IP 和域名加上,etcd的commonName也要設(shè)置成node name。
看最后生成的證書信息:
apiserver:
[root@iZ2ze4ry74x8bh4cweeg69Z pki]# openssl x509 -in /etc/kubernetes/pki/apiserver.crt -text -noout Certificate: ... Signature Algorithm: sha256WithRSAEncryption Issuer: CN=kubernetes Validity Not Before: Mar 31 09:18:06 2020 GMT Not After : Mar 8 09:18:06 2119 GMT Subject: CN=kube-apiserver ... X509v3 extensions: X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Server Authentication X509v3 Subject Alternative Name: DNS:iz2ze4ry74x8bh4cweeg69z, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, DNS:apiserver.cluster.local, DNS:apiserver.cluster.local, IP Address:10.96.0.1, IP Address:172.16.9.192, IP Address:127.0.0.1, IP Address:172.16.9.192, IP Address:172.16.9.193, IP Address:172.16.9.194, IP Address:10.103.97.2 Signature Algorithm: sha256WithRSAEncryption
etcd server:
[root@iZ2ze4ry74x8bh4cweeg69Z pki]# openssl x509 -in /etc/kubernetes/pki/etcd/server.crt -text -noout Certificate: Data: Version: 3 (0x2) Serial Number: 1930981199811083392 (0x1acc392ba2b27c80) Signature Algorithm: sha256WithRSAEncryption Issuer: CN=etcd-ca Validity Not Before: Mar 31 09:18:07 2020 GMT Not After : Mar 8 09:18:07 2119 GMT Subject: CN=iz2ze4ry74x8bh4cweeg69z ... X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication X509v3 Subject Alternative Name: DNS:iz2ze4ry74x8bh4cweeg69z, DNS:localhost, IP Address:172.16.9.192, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1 Signature Algorithm: sha256WithRSAEncryption
現(xiàn)在有個(gè)實(shí)習(xí)生fanux來公司了,也想用用k8s,果斷不放心把a(bǔ)dmin 的kubeconfig交給他,那怎么辦? 有了上面基礎(chǔ),再進(jìn)一步教你怎么為fanux分配一個(gè)單獨(dú)的kubeconfig
從磁盤加載根證書,和私鑰
生成fanux這個(gè)用戶的證書, common name就是fanux
編碼成pem格式
寫kubeconfig, 寫磁盤
func GenerateKubeconfig(conf Config) error{ certs, err := cert.CertsFromFile(conf.CACrtFile) caCert := certs[0] cert := EncodeCertPEM(caCert) caKey,err := TryLoadKeyFromDisk(conf.CAKeyFile) // 這里conf.User就是fanux, conf.Groups就是用戶組,可以是多個(gè) clientCert,clientKey,err := NewCertAndKey(caCert,caKey,conf.User,conf.Groups,conf.DNSNames,conf.IPAddresses) encodedClientKey,err := keyutil.MarshalPrivateKeyToPEM(clientKey) encodedClientCert := EncodeCertPEM(clientCert) // 構(gòu)建kubeconfig的三元組信息 config := &api.Config{ Clusters: map[string]*api.Cluster{ conf.ClusterName: { Server: conf.Apiserver, // 集群地址 如 https://apiserver.cluster.local:6443 CertificateAuthorityData: cert, // pem格式的根證書,用于https }, }, Contexts: map[string]*api.Context{ ctx: { // 三元組信息,用戶名 fanux, 上面的cluster名,以及namespace這里沒寫 Cluster: conf.ClusterName, AuthInfo: conf.User, }, }, AuthInfos: map[string]*api.AuthInfo{ // 用戶信息, 所以你直接改kubeconfig里的user是沒用的,因?yàn)閗8s只認(rèn)證書里的名字 conf.User:&api.AuthInfo{ ClientCertificateData: encodedClientCert, // pem格式的用戶證書 ClientKeyData: encodedClientKey, // pem格式的用戶私鑰 }, }, CurrentContext: ctx, // 當(dāng)前上下文, kubeconfig可以很好支持多用戶和多集群 } err = clientcmd.WriteToFile(*config, conf.OutPut) return nil }
用戶證書和私鑰生成, 和上面簽名證書一樣,user就是fanux, group是用戶組:
func NewCertAndKey(caCert *x509.Certificate, caKey crypto.Signer, user string, groups []string, DNSNames []string,IPAddresses []net.IP) (*x509.Certificate, crypto.Signer, error) { key,err := rsa.GenerateKey(rand.Reader, 2048) serial, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64)) certTmpl := x509.Certificate{ Subject: pkix.Name{ CommonName: user, Organization: groups, }, DNSNames: DNSNames, IPAddresses: IPAddresses, SerialNumber: serial, NotBefore: caCert.NotBefore, NotAfter: time.Now().Add(time.Hour * 24 * 365 * 99).UTC(), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, } certDERBytes, err := x509.CreateCertificate(rand.Reader, &certTmpl, caCert, key.Public(), caKey) cert,err := x509.ParseCertificate(certDERBytes) return cert,key,nil }
然后這位小伙伴的kubeconfig就生成了,此時(shí)沒有任何權(quán)限:
kubectl --kubeconfig ./kube/config get pod Error from server (Forbidden): pods is forbidden: User "fanux" cannot list resource "pods" in API group ...
最后發(fā)揮一下RBAC就可以了,這里就直接綁定個(gè)管理員權(quán)限了
kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: user-admin-test subjects: - kind: User name: "fanux" # Name is case sensitive apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: cluster-admin # using admin role apiGroup: rbac.authorization.k8s.io
讀到這里,這篇“kubernetes的證書有哪些”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識(shí)點(diǎn)還需要大家自己動(dòng)手實(shí)踐使用過才能領(lǐng)會(huì),如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。