1. 程式人生 > >k8s中secret解析

k8s中secret解析

pat ati ever ann 數據庫 secret kubectl ets mmu

概覽

Secret是用來保存小片敏感數據的k8s資源,例如密碼,token,或者秘鑰。這類數據當然也可以存放在Pod或者鏡像中,但是放在Secret中是為了更方便的控制如何使用數據,並減少暴露的風險。

用戶可以創建自己的secret,系統也會有自己的secret。

Pod需要先引用才能使用某個secret,Pod有2種方式來使用secret:作為volume的一個域被一個或多個容器掛載;在拉取鏡像的時候被kubelet引用。

內建的Secrets

由ServiceAccount創建的API證書附加的秘鑰

k8s自動生成的用來訪問apiserver的Secret,所有Pod會默認使用這個Secret與apiserver通信

創建自己的Secret

使用kubectl create secret命令創建Secret

假如mougePod要訪問數據庫,需要用戶名密碼,分別存放在2個文件中: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中,並在apiserver創建Secret

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

查看創建結果:

$ 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: <none> Annotations: <none> Type: Opaque Data ==== password.txt: 12 bytes username.txt: 5 bytes

get或describe指令都不會展示secret的實際內容,這是出於對數據的保護的考慮,如果想查看實際內容請繼續往下看。

手動創建Secret

創建一個secret.yaml文件,內容用base64編碼

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

yaml文件內容:

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

創建:

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

解析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

base64解碼:

$ echo MWYyZDFlMmU2N2Rm | base64 --decode
1f2d1e2e67df

使用Secret

secret可以作為數據卷掛載或者作為環境變量暴露給Pod中的容器使用,也可以被系統中的其他資源使用。比如可以用secret導入與外部系統交互需要的證書文件等。

在Pod中以文件的形式使用secret

  1. 創建一個Secret,多個Pod可以引用同一個Secret
  2. 修改Pod的定義,在spec.volumes[]加一個volume,給這個volume起個名字,spec.volumes[].secret.secretName記錄的是要引用的Secret名字
  3. 在每個需要使用Secret的容器中添加一項spec.containers[].volumeMounts[],指定spec.containers[].volumeMounts[].readOnly = truespec.containers[].volumeMounts[].mountPath要指向一個未被使用的系統路徑。
  4. 修改鏡像或者命令行使系統可以找到上一步指定的路徑。此時Secret中data字段的每一個key都是指定路徑下面的一個文件名

下面是一個Pod中引用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中的多個容器都要引用這個Secret那麽每一個容器定義中都要指定自己的volumeMounts,但是Pod定義中聲明一次spec.volumes就好了。

映射secret key到指定的路徑

可以控制secret key被映射到容器內的路徑,利用spec.volumes[].secret.items來修改被映射的具體路徑

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被映射到了文件/etc/foo/my-group/my-username而不是/etc/foo/username
  • password沒有變

Secret文件權限

可以指定secret文件的權限,類似linux系統文件權限,如果不指定默認權限是0644,等同於linux文件的-rw-r--r--權限

設置默認權限位

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路徑,每一個key衍生出的文件,權限位都將是0400

由於JSON不支持八進制數字,因此用十進制數256表示0400,如果用yaml格式的文件那麽就很自然的使用八進制了

同理可以單獨指定某個key的權限

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

從volume中讀取secret的值

值得註意的一點是,以文件的形式掛載到容器中的secret,他們的值已經是經過base64解碼的了,可以直接讀出來使用。

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

被掛載的secret內容自動更新

也就是如果修改一個Secret的內容,那麽掛載了該Secret的容器中也將會取到更新後的值,但是這個時間間隔是由kubelet的同步時間決定的。最長的時間將是一個同步周期加上緩存生命周期(period+ttl)

特例:以subPath形式掛載到容器中的secret將不會自動更新

以環境變量的形式使用Secret

  1. 創建一個Secret,多個Pod可以引用同一個Secret
  2. 修改pod的定義,定義環境變量並使用env[].valueFrom.secretKeyRef指定secret和相應的key
  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

容器中讀取環境變量,已經是base64解碼後的值了:

$ echo $SECRET_USERNAME
admin
$ echo $SECRET_PASSWORD
1f2d1e2e67df

使用imagePullSecrets

創建一個專門用來訪問鏡像倉庫的secret,當創建Pod的時候由kubelet訪問鏡像倉庫並拉取鏡像,具體描述文檔在 這裏

設置自動導入的imagePullSecrets

可以手動創建一個,然後在serviceAccount中引用它。所有經過這個serviceAccount創建的Pod都會默認使用關聯的imagePullSecrets來拉取鏡像,參考文檔

自動掛載手動創建的Secret

參考文檔

詳情

限制

需要被掛載到Pod中的secret需要提前創建,否則會導致Pod創建失敗

secret是有命名空間屬性的,只有在相同namespace的Pod才能引用它

單個Secret容量限制的1Mb,這麽做是為了防止創建超大的Secret導致apiserver或kubelet的內存耗盡。但是創建過多的小容量secret同樣也會耗盡內存,這個問題在將來可能會有方案解決

kubelet只支持由API server創建出來的Pod中引用secret,使用特殊方式創建出來的Pod是不支持引用secret的,比如通過kubelet的--manifest-url參數創建的pod,或者--config參數創建的,或者REST API創建的。

通過secretKeyRef引用一個不存在你secret key會導致pod創建失敗

用例

Pod中的ssh keys

創建一個包含ssh keys的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

創建一個Pod,其中的容器可以用volume的形式使用ssh keys

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"

Pod中區分生產和測試證書

創建2種不同的證書,分別用在生產和測試環境

$ 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

再創建2個不同的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

以“.”開頭的key可以產生隱藏文件

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: k8s.gcr.io/busybox
    command:
    - ls
    - "-l"
    - "/etc/secret-volume"
    volumeMounts:
    - name: secret-volume
      readOnly: true
      mountPath: "/etc/secret-volume"

會在掛載目錄下產生一個隱藏文件,/etc/secret-volume/.secret-file

最佳實踐

風險

k8s中secret解析