自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

關(guān)于 Kubernetes 的 Secret 并不安全這件事

系統(tǒng) Linux
Secret 其實(shí)并不安全,稍微用 kubectl 查看過 Secret 的人都知道,我們可以非常方便的看到 Secret 的原文,只要有相關(guān)的權(quán)限即可,盡管它的內(nèi)容是 base64 編碼的,這基本上等同于明文。

 K8s 提供了 Secret 資源供我們來保存、設(shè)置一些敏感信息,比如 API endpoint 地址,各種用戶密碼或 token 之類的信息。在沒有使用 K8s 的時(shí)候,這些信息可能是通過配置文件或者環(huán)境變量在部署的時(shí)候設(shè)置的。

不過,Secret 其實(shí)并不安全,稍微用 kubectl 查看過 Secret 的人都知道,我們可以非常方便的看到 Secret 的原文,只要有相關(guān)的權(quán)限即可,盡管它的內(nèi)容是 base64 編碼的,這基本上等同于明文。

所以說,K8s 原生的 Secret 是非常簡(jiǎn)單的,不是特別適合在大型公司里直接使用,對(duì) RBAC 的挑戰(zhàn)也比較大,很多不該看到明文信息的人可能都能看到。

尤其是現(xiàn)在很多公司采用了所謂的 GitOps 理念,很多東西都需要放到 VCS,比如 git 中,這一問題就更日益突出,因?yàn)?VCS 也得需要設(shè)置必要的權(quán)限。

問題

簡(jiǎn)單來說,大概有幾個(gè)地方都可以讓不應(yīng)該看到 Secret 內(nèi)容的人獲得 Secret 內(nèi)容:

  •  etcd 存儲(chǔ)
  •  通過 API server
  •  在 node 上直接查看文件

這里我們以這個(gè)例子來看一下 Secret 在 K8s 中的使用情況。

Secret 定義, 用戶名和密碼分別為 admin 和 hello-secret: 

  1. apiVersion: v1  
  2. kind: Secret  
  3. metadata:  
  4.   name: mysecret  
  5. type: Opaque  
  6. data:  
  7.   username: YWRtaW4 
  8.   passwordaGVsbG8tc2VjcmV0Cg== 

Pod 定義,這里我們將 Secret 作為 volume mount 到容器中。 

  1. apiVersion: v1  
  2. kind: Pod  
  3. metadata:  
  4.   name: mypod  
  5. spec:  
  6.   containers:  
  7.   - name: mypod  
  8.     image: docker.io/containerstack/alpine-stress  
  9.     command:  
  10.       - top  
  11.     volumeMounts:  
  12.     - name: foo  
  13.       mountPath: "/etc/foo"  
  14.       readOnly: true  
  15.   volumes:  
  16.   - name: foo  
  17.     secret:  
  18.       secretName: mysecret 

Pod 啟動(dòng)后,我們可以到容器中來查看 Secret 作為 volume mount 到容器后的文件內(nèi)容。 

  1. $ kubectl exec -it mypod sh  
  2. / # cd /etc/foo/  
  3. /etc/foo # ls -tal  
  4. total 4  
  5. drwxr-xr-x    1 root     root          4096 Apr 14 08:55 ..  
  6. drwxrwxrwt    3 root     root           120 Apr 14 08:55 .  
  7. drwxr-xr-x    2 root     root            80 Apr 14 08:55 ..2021_04_14_08_55_54.401661151  
  8. lrwxrwxrwx    1 root     root            31 Apr 14 08:55 ..data -> ..2021_04_14_08_55_54.401661151  
  9. lrwxrwxrwx    1 root     root            15 Apr 14 08:55 password -> ..data/password  
  10. lrwxrwxrwx    1 root     root            15 Apr 14 08:55 username -> ..data/username  
  11. /etc/foo # ls -tal ..2021_04_14_08_55_54.401661151  
  12. total 8  
  13. drwxr-xr-x    2 root     root            80 Apr 14 08:55 .  
  14. drwxrwxrwt    3 root     root           120 Apr 14 08:55 ..  
  15. -rw-r--r--    1 root     root            13 Apr 14 08:55 password  
  16. -rw-r--r--    1 root     root             5 Apr 14 08:55 username  
  17. /etc/foo # cat password  
  18. hello-secret  
  19. /etc/foo #  

etcd 存儲(chǔ)

API server 中的資源都保存在 etcd 中,我們可以直接從文件看到相關(guān)內(nèi)容: 

  1. # hexdump -C /var/lib/etcd/member/snap/db | grep -A 5 -B 5 hello  
  2. 00043640  12 00 1a 07 64 65 66 61  75 6c 74 22 00 2a 24 32  |....default".*$2|  
  3. 00043650  35 66 37 35 38 30 38 2d  37 33 31 33 2d 34 38 64  |5f75808-7313-48d|  
  4. 00043660  39 2d 39 61 38 65 2d 38  61 35 66 66 32 32 63 64  |9-9a8e-8a5ff22cd|  
  5. 00043670  64 35 39 32 00 38 00 42  08 08 98 dc da 83 06 10  |d592.8.B........|  
  6. 00043680  00 7a 00 12 19 0a 08 70  61 73 73 77 6f 72 64 12  |.z.....password.|  
  7. 00043690  0d 68 65 6c 6c 6f 2d 73  65 63 72 65 74 0a 12 11  |.hello-secret...|  
  8. 000436a0  0a 08 75 73 65 72 6e 61  6d 65 12 05 61 64 6d 69  |..username..admi|  
  9. 000436b0  6e 1a 06 4f 70 61 71 75  65 1a 00 22 00 00 00 00  |n..Opaque.."....|  
  10. 000436c0  00 00 00 08 95 5f 00 00  00 00 00 00 00 00 0a 37  |....._.........7|  
  11. 000436d0  2f 72 65 67 69 73 74 72  79 2f 73 65 72 76 69 63  |/registry/servic|  
  12. 000436e0  65 73 2f 65 6e 64 70 6f  69 6e 74 73 2f 6b 75 62  |es/endpoints/kub| 

可以看到,基本 yaml 中的內(nèi)容都是明文存放的,而且是進(jìn)行 base64 解碼之后的內(nèi)容。

使用下面的命令也可以獲得類似的結(jié)果。 

  1. ETCDCTL_API=3 etcdctl get --prefix /registry/secrets/default/mysecret | hexdump -C 

etcd 本來存儲(chǔ)的是明文數(shù)據(jù),這個(gè)好像已經(jīng)從 1.7 開始支持加密存儲(chǔ)了,而且直接訪問 etcd 從物理上來說也不是那么容易。

API server

通過 API server 則簡(jiǎn)單的多,只要有權(quán)限就可以從任何節(jié)點(diǎn)上通過訪問 API server 來得到 secret 的明文內(nèi)容。 

  1. $ kubectl get secret mysecret -o yaml  
  2. apiVersion: v1  
  3. data:  
  4.   password: aGVsbG8tc2VjcmV0Cg==  
  5.   username: YWRtaW4 
  6. kind: Secret  
  7. metadata:  
  8.   creationTimestamp: "2021-04-14T08:55:52Z"  
  9.   name: mysecret  
  10.   namespace: default  
  11.   resourceVersion: "2196"  
  12.   selfLink: /api/v1/namespaces/default/secrets/mysecret  
  13.   uid: 25f75808-7313-48d9-9a8e-8a5ff22cdd59  
  14. type: Opaque 

節(jié)點(diǎn)上

在節(jié)點(diǎn)上也可以看到 Secret 文件的內(nèi)容。

查找 foo volume 的掛載點(diǎn): 

  1. # mount | grep foo   
  2. tmpfs on /var/lib/kubelet/pods/280451e8-512b-489c-b5dd-df2b1a3c9b29/volumes/kubernetes.io~secret/foo type tmpfs (rw,relatime)  

查看這個(gè) volume 下面的文件內(nèi)容: 

  1. # ls -tal /var/lib/kubelet/pods/280451e8-512b-489c-b5dd-df2b1a3c9b29/volumes/kubernetes.io~secret/foo  
  2. total 4  
  3. drwxrwxrwt 3 root root  120 4月  14 16:55 .  
  4. drwxr-xr-x 2 root root   80 4月  14 16:55 ..2021_04_14_08_55_54.401661151  
  5. lrwxrwxrwx 1 root root   31 4月  14 16:55 ..data -> ..2021_04_14_08_55_54.401661151  
  6. lrwxrwxrwx 1 root root   15 4月  14 16:55 password -> ..data/password  
  7. lrwxrwxrwx 1 root root   15 4月  14 16:55 username -> ..data/username  
  8. drwxr-xr-x 4 root root 4096 4月  14 16:55 ..  
  9. # cat /var/lib/kubelet/pods/280451e8-512b-489c-b5dd-df2b1a3c9b29/volumes/kubernetes.io~secret/foo/password  
  10. hello-secret 

第三方方案

針對(duì)上面提到的可能泄露 Secret 的幾點(diǎn),大概解決方案不難想到如下幾種:

  •  etcd 加密
  •  API server 嚴(yán)格進(jìn)行權(quán)限設(shè)計(jì)
  •  強(qiáng)化 node 節(jié)點(diǎn)用戶權(quán)限管理和系統(tǒng)安全

不過,要相關(guān)確保 Secret 的絕對(duì)安全,上面這幾種方案都是必須,缺一不可,缺少了那一個(gè)都相當(dāng)于在墻上留了一個(gè)洞。

社區(qū)和公有云提供商都有一些產(chǎn)品和方案,我們可以參考一下。

  •  shyiko/kubesec: Secure Secret management for Kubernetes (with gpg, Google Cloud KMS and AWS KMS backends)
  •  bitnami-labs/sealed-secrets: A Kubernetes controller and tool for one-way encrypted Secrets
  •  Vault by HashiCorp
  •  mozilla/sops
  •  Kubernetes External Secrets
  •  Kamus

shyiko/kubesec

kubesec 只對(duì) Secret 中數(shù)據(jù)進(jìn)行加密/解密,支持如下 key 管理服務(wù)或軟件:

  •  AWS Key Management Service
  •  Google Cloud KMS
  •  GnuPG

bitnami-labs/sealed-secrets

Bitnami 在 K8s 領(lǐng)域也是一家人人知曉的公司,輸出了很多技術(shù)和最佳實(shí)踐。

本圖來自 Sealed Secrets: Protecting your passwords before they reach Kubernetes

SealeSecret 將 secret 資源整個(gè)加密保存為 SealedSecret 資源,而解密只能由該集群中的 controller 進(jìn)行。

SealeSecret 提供了一個(gè) kubeseal 工具來對(duì) secret 資源進(jìn)行加密,這個(gè)過程需要一個(gè)公開 key(公鑰),這個(gè)公開 key 就是從 SealeSecret controller 拿到的。

不過,只從從說明文檔來看, SealeSecret controller 加密解密所依賴的 key,也是通過普通的 Secret 來保存的,這難道不是一個(gè)問題?同時(shí)也增加了 SealeSecret controller 的運(yùn)維成本。

mozilla/sops

嚴(yán)格來說, sops 跟 K8s 并沒有什么必然關(guān)系,它只是一個(gè)支持 YAML/JSON/ENV/INI 等文件格式的加密文件編輯器,它支持 AWS KMS, GCP KMS, Azure Key Vault, age, 和 PGP 等服務(wù)與應(yīng)用。

如果有有興趣可以看它的主頁。

Kubernetes External Secrets

Kubernetes External Secrets 是知名域名服務(wù)提供商 godaddy 開發(fā)的開源軟件,它可以直接將保存在外部 KMS 中的機(jī)密信息傳給 K8s 。目前支持的 KSM 包括:

  •  AWS Secrets Manager
  •  AWS System Manager
  •  Hashicorp Vault
  •  Azure Key Vault
  •  GCP Secret Manager
  • Alibaba Cloud KMS Secret Manager

它通過自定義 controller 和 CRD 來實(shí)現(xiàn),具體架構(gòu)圖如下:

具體來說用戶需要?jiǎng)?chuàng)建一個(gè) ExternalSecret 類型的資源,來將外部 KMS 的數(shù)據(jù)映射到 K8s 的 Secret 上。

不過,這種方式大概只有兩點(diǎn)好處:

  • 統(tǒng)一 key 的管理,或者沿用既有 key 資產(chǎn)
  • key 信息不想放到 VCS 等

對(duì)于防止 Sercet 信息泄露,作用不大,因?yàn)槠涿魑馁Y源還是可以在 API server/etcd 上看到。

或者說,External Secrets 真正做的事情,也就是從外部 KMS 中的 key ,映射成 K8s 中的 Secret 資源而已,對(duì)保證在 K8s 集群中數(shù)據(jù)的安全性用處不大。

Kamus

Kamus 同樣提供了加密 key 的方法(一個(gè)命令行工具),同時(shí)只有通過 K8s 中的 controller 才能對(duì)這個(gè) key 進(jìn)行解密。不過它 保存在 K8s 中的 Secret 是加密的狀態(tài),用戶不能像 External Secrets 那樣直接獲得 Secret 的明文內(nèi)容。

Kamus 由 3 個(gè)組件組成,分別是:

  •  Encrypt API
  •  Decrypt API
  •  Key Management System (KMS)

KMS 是一個(gè)外部加密服務(wù)的封裝,目前支持如下服務(wù):- AES - AWS KMS - Azure KeyVault - Google Cloud KMS

Kamus 以 service account 為單位對(duì) secret 進(jìn)行加密,之后 Pod 會(huì)通過 service account 來請(qǐng)求 Kamus 的解密服務(wù)來對(duì)該 secret 進(jìn)行解密。

對(duì) K8s 來說,解密 secret 可以通過 init container 來實(shí)現(xiàn):定義一個(gè) 基于內(nèi)存的 emptyDir ,業(yè)務(wù)容器和 init 容器使用同一個(gè) volume, init 容器解密后,將數(shù)據(jù)存放到該 volume 下,之后業(yè)務(wù)容器就可以使用解密后的 secret 數(shù)據(jù)了。

Vault by HashiCorp

HashiCorp 公司就不多說,在云計(jì)算/DevOps領(lǐng)域也算是數(shù)一數(shù)二的公司了。

Vault 本身就是一個(gè) KMS 類似的服務(wù),用于管理機(jī)密數(shù)據(jù)。對(duì)于 K8s 的原生 secret ,大概提供了如下兩種方式的支持:

  •  Agent Sidecar Injector/vault-k8s
  •  Vault CSI Provider

Agent Sidecar Injector

這種方式和上面的 Kamus 類似,也是需要兩個(gè)組件:

  •  Mutation webhook:

          負(fù)責(zé)修改 pod 定義,注入init/sidecar

  •  agent-sidecar:

           負(fù)責(zé)獲取和解密數(shù)據(jù),將數(shù)據(jù)保存到指定的 volume/路徑下

Vault agent sidecar injector 不僅提供了 init container 來初始化 secret ,還通過 sidecar 來定期更新 secret ,這樣就非常接近原生 secret 的實(shí)現(xiàn)了。

應(yīng)用程序則只需要在文件系統(tǒng)上讀取指定的文件就可以了,而不必關(guān)系如何從外部獲取加密信息。

這是官方 blog 中的一個(gè)示例:

Pod 信息: 

  1. spec:  
  2.   template:  
  3.     metadata:  
  4.       annotations:  
  5.         vault.hashicorp.com/agent-inject: "true"  
  6.         vault.hashicorp.com/agent-inject-secret-helloworld: "secrets/helloworld"  
  7.         vault.hashicorp.com/role: "myapp" 

這個(gè)定義中,vault-k8s 會(huì)對(duì)該 pod 注入 vault agent,并使用 secrets/helloworld 來初始化。Pod 運(yùn)行后,可以在 /vault/secrets 下找到一個(gè)名為 helloworld 的文件。 

  1. $ kubectl exec -ti app-XXXXXXXXX -c app -- cat /vault/secrets/helloworld  
  2. data: map[password:foobarbazpass username:foobaruser]  
  3. metadata: map[created_time:2019-12-16T01:01:58.869828167Z deletion_time: destroyed:false version:1] 

當(dāng)然這個(gè)數(shù)據(jù)是raw data,沒有經(jīng)過格式化。如果想指定輸出到文件中的格式,可以使用 vault 的模板功能。

Vault CSI Provider

這部分可以參考下面的社區(qū)方案部分。

社區(qū)方案

當(dāng)然,社區(qū)沒有理由意識(shí)不到原生 secret 的問題,因此社區(qū)也有 Kubernetes Secrets Store CSI Driver ,一種通過 CSI 接口將 Secret 集成到 K8s 的方案。

Secrets Store CSI driver(secrets-store.csi.k8s.io)可以讓 K8s mount 多個(gè) secret 以 volume 的形式,從外部 KMS mount 到 Pod 里。

要想使用 Secrets Store CSI Driver ,大致過程如下:

  •  定義 SecretProviderClass 
  1. apiVersion: secrets-store.csi.x-k8s.io/v1alpha1  
  2. kind: SecretProviderClass  
  3. metadata:  
  4.   name: my-provider  
  5. spec: 
  6.   provider: vault   # accepted provider options: azure or vault or gcp  
  7.   parameters:       # provider-specific parameters 
  •  為 Pod 配置 Volume 
  1. kind: Pod  
  2. apiVersion: v1  
  3. metadata:  
  4.   name: secrets-store-inline  
  5. spec:  
  6.   containers:  
  7.   - image: k8s.gcr.io/e2e-test-images/busybox:1.29  
  8.     name: busybox  
  9.     command:  
  10.     - "/bin/sleep"  
  11.     - "10000"  
  12.     volumeMounts:  
  13.     - name: secrets-store-inline  
  14.       mountPath: "/mnt/secrets-store"  
  15.       readOnly: true  
  16.   volumes:  
  17.     - name: secrets-store-inline  
  18.       csi:  
  19.         driver: secrets-store.csi.k8s.io  
  20.         readOnly: true  
  21.         volumeAttributes:  
  22.           secretProviderClass: "my-provider" 

Pod 啟動(dòng)之后,就可以確認(rèn)解密后的數(shù)據(jù)了: 

  1. $ kubectl exec secrets-store-inline -- ls /mnt/secrets-store/  
  2. foo 

總結(jié)

上面的總結(jié)都是基于互聯(lián)網(wǎng)公開的資料而來,并沒有經(jīng)過親身體驗(yàn),因此有些地方可能理解有誤,要想深入了解還需要自己親手確認(rèn)最好。

不過總體來說,社區(qū)這種方案可能最簡(jiǎn)單,部署也不是很麻煩,只是這就和原生的 secret 基本沒什么關(guān)系了。。。 

 

責(zé)任編輯:龐桂玉 來源: 奇妙的Linux世界
相關(guān)推薦

2015-05-27 13:19:23

2020-11-13 11:27:04

短信驗(yàn)證

2015-07-01 14:48:51

2015-07-06 14:32:10

2018-03-12 13:47:27

2023-10-19 10:43:38

2023-06-01 19:24:16

2018-07-10 08:56:19

編程程序員開發(fā)

2012-04-16 10:12:54

Java線程

2009-08-03 16:58:59

C#不安全代碼

2024-01-19 08:42:45

Java線程字符串

2020-11-03 12:32:25

影子物聯(lián)網(wǎng)物聯(lián)網(wǎng)IOT

2021-04-04 23:16:52

安全刷臉銀行

2020-04-22 20:35:02

HashMap線程安全

2014-09-12 17:44:23

2023-05-12 17:45:15

MySQL索引排序

2018-01-26 10:49:19

2014-04-09 09:37:29

2020-04-23 10:36:45

容器云安全云計(jì)算

2010-08-16 10:01:01

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)