Kubernetes中的Secret配置


 

Secret 概覽

Secret 是一種包含少量敏感信息例如密碼、token 或 key 的對象。這樣的信息可能會被放在 Pod spec 中或者鏡像中;將其放在一個 secret 對象中可以更好地控制它的用途,並降低意外暴露的風險。

用戶可以創建 secret,同時系統也創建了一些 secret。

要使用 secret,pod 需要引用 secret。Pod 可以用兩種方式使用 secret:作為 volume
中的文件被掛載到 pod 中的一個或者多個容器里,或者當 kubelet 為 pod 拉取鏡像時使用。

對象類型用來保存敏感信息,例如密碼、OAuth 令牌和 ssh key。將這些信息放在 secret
中比放在 pod
的定義中或者 docker 鏡像中來說更加安全和靈活。參閱 Secret 設計文檔
獲取更多詳細信息。

內置 secret

SERVICE ACCOUNT 使用 API 憑證自動創建和附加 SECRET

Kubernetes 自動創建包含訪問 API 憑據的 secret,並自動修改您的 pod 以使用此類型的 secret。

如果需要,可以禁用或覆蓋自動創建和使用API憑據。但是,如果您需要的只是安全地訪問 apiserver,我們推薦這樣的工作流程。

參閱 Service Account
文檔獲取關於 Service Account 如何工作的更多信息。

創建您自己的 Secret

使用 KUBECTL 創建 SECRET

假設有些 pod 需要訪問數據庫。這些 pod 需要使用的用戶名和密碼在您本地機器的 ./username.txt
和 ./password.txt
文件里。

# Create files needed for rest of example.
$ echo -n "admin" > ./username.txt
$ echo -n "1f2d1e2e67df" > ./password.txt

kubectl create secret
命令將這些文件打包到一個 Secret 中並在 API server 中創建了一個對象。

$ kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt
secret "db-user-pass" created

您可以這樣檢查剛創建的 secret:

$ kubectl get secrets
NAME                  TYPE                                  DATA      AGE
db-user-pass          Opaque                                2         51s

$ kubectl describe secrets/db-user-pass
Name:            db-user-pass
Namespace:       default
Labels:          
Annotations:     

Type:            Opaque

Data
====
password.txt:    12 bytes
username.txt:    5 bytes

 

請注意,默認情況下, get
和 describe
命令都不會顯示文件的內容。這是為了防止將 secret 中的內容被意外暴露給從終端日志記錄中刻意尋找它們的人。

請參閱 解碼 secret
了解如何查看它們的內容。

手動創建 SECRET

您也可以先以 json 或 yaml 格式在文件中創建一個 secret 對象,然后創建該對象。

每一項必須是 base64 編碼:

$ echo -n "admin" | base64
YWRtaW4=
$ echo -n "1f2d1e2e67df" | base64
MWYyZDFlMmU2N2Rm

現在可以像這樣寫一個 secret 對象:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm

 

數據字段是一個映射。它的鍵必須匹配 DNS_SUBDOMAIN
,前導點也是可以的。這些值可以是任意數據,使用 base64 進行編碼。

使用 
kubectl create

創建 secret:

$ kubectl create -f ./secret.yaml
secret "mysecret" created

編碼注意:secret 數據的序列化 JSON 和 YAML 值使用 base64 編碼成字符串。換行符在這些字符串中無效,必須省略。當在Darwin/OS X上使用 base64
實用程序時,用戶應避免使用 -b
選項來拆分長行。另外,對於 Linux用戶如果 -w
選項不可用的話,應該添加選項 -w 0
到 base64
命令或管道 base64 | tr -d 'n'

刪除secret:

$ kubectl delete -f ./secret.yaml

解碼 SECRET

可以使用 kubectl get secret
命令獲取 secret。例如,獲取在上一節中創建的 secret:

$ kubectl get secret mysecret -o yaml
apiVersion: v1
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm
kind: Secret
metadata:
  creationTimestamp: 2016-01-22T18:41:56Z
  name: mysecret
  namespace: default
  resourceVersion: "164619"
  selfLink: /api/v1/namespaces/default/secrets/mysecret
  uid: cfee02d6-c137-11e5-8d73-42010af00002
type: Opaque

解碼密碼字段:

$ echo "MWYyZDFlMmU2N2Rm" | base64 --decode
1f2d1e2e67df

使用 Secret

Secret 可以作為數據卷被掛載,或作為環境變量暴露出來以供 pod 中的容器使用。它們也可以被系統的其他部分使用,而不直接暴露在 pod 內。例如,它們可以保存憑據,系統的其他部分應該用它來代表您與外部系統進行交互。

在 POD 中使用 SECRET 文件

在 Pod 中的 volume 里使用 Secret:

  1. 創建一個 secret 或者使用已有的 secret。多個 pod 可以引用同一個 secret。
  2. 修改您的 pod 的定義在 spec.volumes[]
    下增加一個 volume。可以給這個 volume 隨意命名,它的 spec.volumes[].secret.secretName
    必須等於 secret 對象的名字。
  3. 將 spec.containers[].volumeMounts[]
    加到需要用到該 secret 的容器中。指定 spec.containers[].volumeMounts[].readOnly = true
    和 spec.containers[].volumeMounts[].mountPath
    為您想要該 secret 出現的尚未使用的目錄。
  4. 修改您的鏡像並且/或者命令行讓程序從該目錄下尋找文件。Secret 的 data
    映射中的每一個鍵都成為了 mountPath
    下的一個文件名。

這是一個在 pod 中使用 volume 掛在 secret 的例子:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret

您想要用的每個 secret 都需要在 spec.volumes
中指明。

如果 pod 中有多個容器,每個容器都需要自己的 volumeMounts
配置塊,但是每個 secret 只需要一個 spec.volumes

您可以打包多個文件到一個 secret 中,或者使用的多個 secret,怎樣方便就怎樣來。

向特性路徑映射 SECRET 密鑰

我們還可以控制 Secret key 映射在 volume 中的路徑。您可以使用 spec.volumes[].secret.items
字段修改每個 key 的目標路徑:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      items:
      - key: username
        path: my-group/my-username

將會發生什么呢:

  • username
    secret 存儲在 /etc/foo/my-group/my-username
    文件中而不是 /etc/foo/username
    中。
  • password
    secret 沒有被影射

如果使用了 spec.volumes[].secret.items
,只有在 items
中指定的 key 被影射。要使用 secret 中所有的 key,所有這些都必須列在 items
字段中。所有列出的密鑰必須存在於相應的 secret 中。否則,不會創建卷。

SECRET 文件權限

您還可以指定 secret 將擁有的權限模式位文件。如果不指定,默認使用 0644
。您可以為整個保密卷指定默認模式,如果需要,可以覆蓋每個密鑰。

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      defaultMode: 256

然后,secret 將被掛載到 /etc/foo
目錄,所有通過該 secret volume 掛載創建的文件的權限都是 0400

請注意,JSON 規范不支持八進制符號,因此使用 256 值作為 0400 權限。如果您使用 yaml 而不是 json 作為 pod,則可以使用八進制符號以更自然的方式指定權限。

您還可以是用映射,如上一個示例,並為不同的文件指定不同的權限,如下所示:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      items:
      - key: username
        path: my-group/my-username
        mode: 511

在這種情況下,導致 /etc/foo/my-group/my-username
的文件的權限值為 0777
。由於 JSON 限制,必須以十進制格式指定模式。

請注意,如果稍后閱讀此權限值可能會以十進制格式顯示。

從 VOLUME 中消費 SECRET 值

在掛載的 secret volume 的容器內,secret key 將作為文件,並且 secret 的值使用 base-64 解碼並存儲在這些文件中。這是在上面的示例容器內執行的命令的結果:

$ ls /etc/foo/
username
password
$ cat /etc/foo/username
admin
$ cat /etc/foo/password
1f2d1e2e67df

容器中的程序負責從文件中讀取 secret。

掛載的 SECRET 被自動更新

當已經在 volume 中消被消費的 secret 被更新時,被映射的 key 也將被更新。

Kubelet 在周期性同步時檢查被掛載的 secret 是不是最新的。但是,它正在使用其基於本地 ttl 的緩存來獲取當前的 secret 值。結果是,當 secret 被更新的時刻到將新的 secret 映射到 pod 的時刻的總延遲可以與 kubelet 中的secret 緩存的 kubelet sync period + ttl 一樣長。

SECRET 作為環境變量

將 secret 作為 pod 中的環境變量使用:

  1. 創建一個 secret 或者使用一個已存在的 secret。多個 pod 可以引用同一個 secret。
  2. 在每個容器中修改您想要使用 secret key 的 Pod 定義,為要使用的每個 secret key 添加一個環境變量。消費secret key 的環境變量應填充 secret 的名稱,並鍵入env[x].valueFrom.secretKeyRef
  3. 修改鏡像並/或者命令行,以便程序在指定的環境變量中查找值。
apiVersion: v1
kind: Pod
metadata:
  name: secret-env-pod
spec:
  containers:
  - name: mycontainer
    image: redis
    env:
      - name: SECRET_USERNAME
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: username
      - name: SECRET_PASSWORD
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: password
  restartPolicy: Never

消費環境變量里的 SECRET 值

在一個消耗環境變量 secret 的容器中,secret key 作為包含 secret 數據的 base-64 解碼值的常規環境變量。這是從上面的示例在容器內執行的命令的結果:

$ echo $SECRET_USERNAME
admin
$ echo $SECRET_PASSWORD
1f2d1e2e67df

使用 IMAGEPULLSECRET

imagePullSecret 是將包含 Docker(或其他)鏡像注冊表密碼的 secret 傳遞給 Kubelet 的一種方式,因此可以代表您的 pod 拉取私有鏡像。

手動指定 IMAGEPULLSECRET

imagePullSecret 的使用在 鏡像文檔
中說明。

安排 imagePullSecrets 自動附加

您可以手動創建 imagePullSecret,並從 serviceAccount 引用它。使用該 serviceAccount 創建的任何 pod 和默認使用該 serviceAccount 的 pod 將會將其的 imagePullSecret 字段設置為服務帳戶的 imagePullSecret 字段。有關該過程的詳細說明,請參閱 將 ImagePullSecrets 添加到服務帳戶

自動掛載手動創建的 SECRET

手動創建的 secret(例如包含用於訪問 github 帳戶的令牌)可以根據其服務帳戶自動附加到 pod。請參閱 使用 PodPreset 向 Pod 中注入信息
以獲取該進程的詳細說明。

詳細

限制

驗證 secret volume 來源確保指定的對象引用實際上指向一個類型為 Secret 的對象。因此,需要在依賴於它的任何 pod 之前創建一個 secret。

Secret API 對象駐留在命名空間中。它們只能由同一命名空間中的 pod 引用。

每個 secret 的大小限制為1MB。這是為了防止創建非常大的 secret 會耗盡 apiserver 和 kubelet 的內存。然而,創建許多較小的 secret 也可能耗盡內存。更全面得限制 secret 對內存使用的更全面的限制是計划中的功能。

Kubelet 僅支持從 API server 獲取的 Pod 使用 secret。這包括使用 kubectl 創建的任何 pod,或間接通過 replication controller 創建的 pod。它不包括通過 kubelet --manifest-url
標志,其 --config
標志或其 REST API 創建的pod(這些不是創建 pod 的常用方法)。

必須先創建 secret,除非將它們標記為可選項,否則必須在將其作為環境變量在 pod 中使用之前創建 secret。對不存在的 secret 的引用將阻止其啟動。

通過 secretKeyRef
對不存在於命名的 key 中的 key 進行引用將阻止該啟動。

用於通過 envFrom
填充環境變量的 secret,這些環境變量具有被認為是無效環境變量名稱的 key 將跳過這些鍵。該 pod 將被允許啟動。將會有一個事件,其原因是 InvalidVariableNames
,該消息將包含被跳過的無效鍵的列表。該示例顯示一個 pod,它指的是包含2個無效鍵,1badkey 和 2alsobad 的默認/mysecret ConfigMap。

$ kubectl get events
LASTSEEN   FIRSTSEEN   COUNT     NAME            KIND      SUBOBJECT                         TYPE      REASON
0s         0s          1         dapi-test-pod   Pod                                         Warning   InvalidEnvironmentVariableNames   kubelet, 127.0.0.1      Keys [1badkey, 2alsobad] from the EnvFrom secret default/mysecret were skipped since they are considered invalid environment variable names.

Secret 與 Pod 生命周期的聯系

通過 API 創建的 Pod 時,不會檢查應用的 secret 是否存在。一旦 Pod 被調度,kubelet 就會嘗試獲取該 secret 的值。如果獲取不到該 secret,或者暫時無法與 API server 建立連接,kubelet 將會定期重試。Kubelet 將會報告關於 pod 的事件,並解釋它無法啟動的原因。一旦獲取的 secret,kubelet將創建並裝載一個包含它的卷。在安裝所有pod的卷之前,都不會啟動 pod 的容器。

使用案例

使用案例:包含 ssh 密鑰的 pod

創建一個包含 ssh key 的 secret:

$ kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=/path/to/.ssh/id_rsa --from-file=ssh-publickey=/path/to/.ssh/id_rsa.pub

安全性注意事項:發送自己的 ssh 密鑰之前要仔細思考:集群的其他用戶可能有權訪問該密鑰。使用您想要共享 Kubernetes 群集的所有用戶可以訪問的服務帳戶,如果它們遭到入侵,可以撤銷。

現在我們可以創建一個使用 ssh 密鑰引用 secret 的pod,並在一個卷中使用它:

kind: Pod
apiVersion: v1
metadata:
  name: secret-test-pod
  labels:
    name: secret-test
spec:
  volumes:
  - name: secret-volume
    secret:
      secretName: ssh-key-secret
  containers:
  - name: ssh-test-container
    image: mySshImage
    volumeMounts:
    - name: secret-volume
      readOnly: true
      mountPath: "/etc/secret-volume"

當容器中的命令運行時,密鑰的片段將可在以下目錄:

/etc/secret-volume/ssh-publickey
/etc/secret-volume/ssh-privatekey

然后容器可以自由使用密鑰數據建立一個 ssh 連接。

使用案例:包含 prod/test 憑據的 pod

下面的例子說明一個 pod 消費一個包含 prod 憑據的 secret,另一個 pod 使用測試環境憑據消費 secret。

創建 secret:

$ kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11
secret "prod-db-secret" created
$ kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests
secret "test-db-secret" created

創建 pod :

apiVersion: v1
kind: List
items:
- kind: Pod
  apiVersion: v1
  metadata:
    name: prod-db-client-pod
    labels:
      name: prod-db-client
  spec:
    volumes:
    - name: secret-volume
      secret:
        secretName: prod-db-secret
    containers:
    - name: db-client-container
      image: myClientImage
      volumeMounts:
      - name: secret-volume
        readOnly: true
        mountPath: "/etc/secret-volume"
- kind: Pod
  apiVersion: v1
  metadata:
    name: test-db-client-pod
    labels:
      name: test-db-client
  spec:
    volumes:
    - name: secret-volume
      secret:
        secretName: test-db-secret
    containers:
    - name: db-client-container
      image: myClientImage
      volumeMounts:
      - name: secret-volume
        readOnly: true
        mountPath: "/etc/secret-volume"

這兩個容器將在其文件系統上顯示以下文件,其中包含每個容器環境的值:

/etc/secret-volume/username
/etc/secret-volume/password

請注意,兩個 pod 的 spec 配置中僅有一個字段有所不同;這有助於使用普通的 pod 配置模板創建具有不同功能的 pod。您可以使用兩個 service account 進一步簡化基本 pod spec:一個名為 prod-user
擁有 prod-db-secret
,另一個稱為 test-user
擁有 test-db-secret
。然后,pod spec 可以縮短為,例如:

kind: Pod
apiVersion: v1
metadata:
  name: prod-db-client-pod
  labels:
    name: prod-db-client
spec:
  serviceAccount: prod-db-client
  containers:
  - name: db-client-container
    image: myClientImage

使用案例:secret 卷中以點號開頭的文件

為了將數據“隱藏”起來(即文件名以點號開頭的文件),簡單地說讓該鍵以一個點開始。例如,當如下 secret 被掛載到卷中:

kind: Secret
apiVersion: v1
metadata:
  name: dotfile-secret
data:
  .secret-file: dmFsdWUtMg0KDQo=
---
kind: Pod
apiVersion: v1
metadata:
  name: secret-dotfiles-pod
spec:
  volumes:
  - name: secret-volume
    secret:
      secretName: dotfile-secret
  containers:
  - name: dotfile-test-container
    image: gcr.io/google_containers/busybox
    command:
    - ls
    - "-l"
    - "/etc/secret-volume"
    volumeMounts:
    - name: secret-volume
      readOnly: true
      mountPath: "/etc/secret-volume"

Secret-volume
將包含一個單獨的文件,叫做 .secret-file
, dotfile-test-container
的 /etc/secret-volume/.secret-file
路徑下將有該文件。

注意

以點號開頭的文件在 ls -l
的輸出中被隱藏起來了;列出目錄內容時,必須使用 ls -la
才能查看它們。

使用案例:Secret 僅對 pod 中的一個容器可見

考慮以下一個需要處理 HTTP 請求的程序,執行一些復雜的業務邏輯,然后使用 HMAC 簽署一些消息。因為它具有復雜的應用程序邏輯,所以在服務器中可能會出現一個未被注意的遠程文件讀取漏洞,這可能會將私鑰暴露給攻擊者。

這可以在兩個容器中分為兩個進程:前端容器,用於處理用戶交互和業務邏輯,但無法看到私鑰;以及可以看到私鑰的簽名者容器,並且響應來自前端的簡單簽名請求(例如通過本地主機網絡)。

使用這種分割方法,攻擊者現在必須欺騙應用程序服務器才能進行任意的操作,這可能比使其讀取文件更難。

最佳實踐

客戶端使用 secret API

當部署與 secret API 交互的應用程序時,應使用諸如 RBAC
之類的 授權策略
來限制訪問。

Secret 的重要性通常不盡相同,其中許多可能只對 Kubernetes 集群內(例如 service account 令牌)和對外部系統造成影響。即使一個應用程序可以理解其期望的與之交互的 secret 的權力,但是同一命名空間中的其他應用程序也可以使這些假設無效。

由於這些原因,在命名空間中 watch
和 list
secret 的請求是非常強大的功能,應該避免這樣的行為,因為列出 secret 可以讓客戶端檢查所有 secret 是否在該命名空間中。在群集中 watch
和 list
所有 secret 的能力應該只保留給最有特權的系統級組件。

需要訪問 secrets API 的應用程序應該根據他們需要的 secret 執行 get
請求。這允許管理員限制對所有 secret 的訪問,同時設置 白名單訪問
應用程序需要的各個實例。

為了提高循環獲取的性能,客戶端可以設計引用 secret 的資源,然后 watch
資源,在引用更改時重新請求 secret。此外,還提出了一種 ”批量監控“ API
來讓客戶端 watch
每個資源,該功能可能會在將來的 Kubernetes 版本中提供。

安全屬性

保護

因為 secret
對象可以獨立於使用它們的 pod
而創建,所以在創建、查看和編輯 pod 的流程中 secret 被暴露的風險較小。系統還可以對 secret
對象采取額外的預防措施,例如避免將其寫入到磁盤中可能的位置。

只有當節點上的 pod 需要用到該 secret 時,該 secret 才會被發送到該節點上。它不會被寫入磁盤,而是存儲在 tmpfs 中。一旦依賴於它的 pod 被刪除,它就被刪除。

在大多數 Kubernetes 項目維護的發行版中,用戶與 API server 之間的通信以及從 API server 到 kubelet 的通信都受到 SSL/TLS 的保護。通過這些通道傳輸時,secret 受到保護。

節點上的 secret 數據存儲在 tmpfs 卷中,因此不會傳到節點上的其他磁盤。

同一節點上的很多個 pod 可能擁有多個 secret。但是,只有 pod 請求的 secret 在其容器中才是可見的。因此,一個 pod 不能訪問另一個 Pod 的 secret。

Pod 中有多個容器。但是,pod 中的每個容器必須請求其掛載卷中的 secret 卷才能在容器內可見。這可以用於 在 Pod 級別構建安全分區

風險

  • API server 的 secret 數據以純文本的方式存儲在 etcd 中;因此:

     

    • 管理員應該限制 admin 用戶訪問 etcd;
    • API server 中的 secret 數據位於 etcd 使用的磁盤上;管理員可能希望在不再使用時擦除/粉碎 etcd 使用的磁盤
  • 如果您將 secret 數據編碼為 base64 的清單(JSON 或 YAML)文件,共享該文件或將其檢入代碼庫,這樣的話該密碼將會被泄露。 Base64 編碼不是一種加密方式,一樣也是純文本。
  • 應用程序在從卷中讀取 secret 后仍然需要保護 secret 的值,例如不會意外記錄或發送給不信任方。
  • 可以創建和使用 secret 的 pod 的用戶也可以看到該 secret 的值。即使 API server 策略不允許用戶讀取 secret 對象,用戶也可以運行暴露 secret 的 pod。
  • 如果運行了多個副本,那么這些 secret 將在它們之間共享。默認情況下,etcd 不能保證與 SSL/TLS 的對等通信,盡管可以進行配置。
  • 目前,任何節點的 root 用戶都可以通過模擬 kubelet 來讀取 API server 中的任何 secret。只有向實際需要它們的節點發送 secret 才能限制單個節點的根漏洞的影響,該功能還在計划中。

參考地址:

https://github.com/rootsongjc/kubernetes.github.io/blob/master/docs/concepts/configuration/secret.md

https://www.colabug.com/1660620.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM