Kubernetes secrets 加密處理的幾種方式 | IDCF

前言

Kubernetes 已經毫無爭議地成爲了雲原生時代的事實標準,在 Kubernetes 上部署應用程序也變得簡單起來(不管是採用 kustomize 仍是 helm),雖然對於敏感信息(好比用戶名、密碼、token 和證書等)的處理,Kubernetes 本身提供了 secret 這種方式,但其是一種編碼方式,而非加密方式,若是須要用版本控制系統(好比 git)來對全部的文件、內容等進行版本控制時,這種用編碼來處理敏感信息的方式就顯得很不安全了(即便是採用私有庫),這一點在實現 GitOps 時,是一個痛點。git

基於此,本文就介紹三種能夠加密 Kubernetes secret 的方式:Sealed Secrets、Helm Secrets 和 Kamus。github

1、Sealed Secrets

Sealed Secrets 充分利用 kuberntes 的高擴展性,經過 CRD 來建立一個 SealedSecret 對象,經過將加密的內容存儲在擴展 SealedSecret 對象中,而 SealedSecret 只可以被運行於目標集羣上的 controller 解密,其餘人員和方式都沒法正確解密原始數據。SealedSecret 對象同時又會生成與其名稱相同的 secret 對象,隨後就能夠按照常規方式使用 secret 對象了。最後將加密後的文件直接推送至版本控制系統便可,而不用擔憂敏感信息被泄漏。redis

1.1 原理

Sealed Secrets 加解密的原理簡單來講就是:安裝的時候 controller 會生成一對用於加密的 key,加密時在客戶端 kubeseal 的幫助下,將包含敏感信息的 kubernets secrets 內容加密轉變爲一個包含有加密信息的 Kubernetes SealedSecrets 對象;解密時在 controller 的幫助下將 Kubernetes SealedSecrets 對象內的內容進行解密,而後生成常規的 kubernetes secret 對象。docker

下面分加解密兩部分(encryption 和 decryption)來介紹 Sealed Secrerts 的工做原理。數據庫

1.2 加密(encryption)

image.png

kubeseal 使用位於 cluster controller 生成的 public key 來加密,將 secrets 對象的內容加密轉變成 SealedSecrets 對象。public key 和 private key 在安裝 controller 的時候,以 secret(以下的 sealed-secrets-key217vf)的造成存放,能夠在安裝 controller 所在的 ns 下面查看。npm

$ kubectl -n kube-system get secret | grep sealed-secret
sealed-secrets-controller-token-qv2n5            kubernetes.io/service-account-token   3      6d4h
sealed-secrets-key2l7vf                          kubernetes.io/tls                     2      6d4h

1.3 解密(decryption)

image.png

當將加密後生成的 SealedSecrets 對象進行部署時(kubectl apply/create),controller 會先拿 private key 進行解密,而後再生成與 SealedSecrets 同名的 Secret 對象,而此時的 Secret 對象保存的是通過 base64 編碼後的信息,隨後能夠像正常使用 secret 同樣使用這些信息。json

SealedSecrets 和 Secret 二者的關係與 Deployment 和 Pod 之間的關係相似。

1.4 安裝

Sealed Secrets 有兩部分組成:segmentfault

  • 安裝於集羣側的 controller
  • 客戶端工具 kubeseal

因此安裝也分兩步,安裝 controller 和 kubeseal。能夠先安裝 controller,執行以下命令便可:api

$ kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.14.1/controller.yaml

隨後查看 kube-system ns 下面的 controller pod:安全

$ kubectl -n kube-system get pods | grep seal
sealed-secrets-controller-64b74f67b-4wtj7   1/1     Running   0          153m

接着安裝客戶端工具 kubeseal。這個能夠根據本身的 OS 來選用不一樣的安裝方式,以 MacOs 爲例:

$ brew install kubeseal

經過查看 kubeseal 的版原本肯定是否安裝成功:

$ kubeseal --version
kubeseal version: v0.14.1

此時,兩部分均安裝成功,下面可使用了。

1.5 使用

選擇一個包含有須要加密的 secret 的文件,內容以下:

apiVersion: v1
data:
  username: eGlhb21hZ2U=
  password: cGFzc3cwcmQ=
  token: MWU0ZGdyNWZncmgzcmZmZ3JodG9ubmhyaHI=
kind: Secret
metadata:
  name: seal-test-secret
  namespace: test
type: Opaque

將上述內容寫入一個 yaml 文件,好比 test-secret.yaml,而後執行以下命令加密:

$ kubeseal < test-secret.yaml > test-seal-secret.yaml

能夠查看 test-seal-secret.yaml 文件的內容:

{
  "kind": "SealedSecret",
  "apiVersion": "bitnami.com/v1alpha1",
  "metadata": {
    "name": "seal-test-secret",
    "namespace": "test",
    "creationTimestamp": null
  },
  "spec": {
    "template": {
      "metadata": {
        "name": "seal-test-secret",
        "namespace": "test",
        "creationTimestamp": null
      },
      "type": "Opaque"
    },
    "encryptedData": {
      "password": "AgCHLFSlGFpX2B9QDhWbMTfT83aopXMisR5XnUPZNcbvvnQzqgyG8fBVknT8LCNF5ExtUCCcNLsvWRrZY+9BJqf5dlBl6DkLV1acuPicP0vuGaUQwmc5BY/5Bj53Oj9uMYLNdVHoQ3E6kQgeJPa5v4rvwRXsB0EneYPcT88KyMg+tn4OY9JH+hpg2XMXZudyyZsocE852J5nfN4P7WZQYaG2eIBqRSQvQXUflQQvZ5wBCkTvmaZYfxz+Lxuf3wDWdSlPjcgSVnn5tWNP7u0ErdVy8LwTL5HzJdcoSviDysTq3VVA8W9Nmn0CM3QS0R0Nbi3JfalUdxfBMK+yb7t6Z72oJyoxGfCa07iKnkM37SSw4vl1nXiYy3FMWuzDtWLVk6XzjBZR2ChoeClqbGDg8KeSWZg/rO3Xku98vCmCa004OetJKBMc9Db3q+gX53ThdU70VvRol9rLPFBPHB8NTjD+Bu0Ss4XzIzZzi8J+Ov5xE7G8LnPLSZtZQyD/qGZK4n7pU1YNLROJ+fz1W5edPdpb5szUOqs1bpFfGleUiPZo1sGA0f1EsDvJShptgtT44YzGRkkgrP1LGp2AVIpnt9meE5WNCoSEPZJVx7wWMV9CHMOyyUi8zi+oG/S2NkI3rc2sC8AFp0DqP9m/HaX1GG+6vw9oHAbhxpR4v3mDyBIq+/8UEMtkybIEDQGHqyQ5CfRow+A80cA4Hw==",
      "token": "AgBRi4NZunaJtHyP5aAoWmGtEXBipbFIb/n4ep8wdg+eka5xbDeLZwNCLofbUL+u0pP/CHSJeWl62mVPJhZdOKE+Su0b8a78im0+xsochaMQf0AI1GGL/Fo08HI8paP8k404gwAtonocIFSis3YooU0nyVD+lYH+k09FGABI+RmVLc2XkuIr96TTL4xsdSM5L0Ks2SFQKcQ42JfFWtNdXz6lr/IODsZop0/xAk6ffbsGGmCUjwusUU3Wp0MR25ntYT8ySuO6W7xkfGozEFzztteBJs28SHLf5HUi6BbYVnsZibrFF3BZP5aNnBg2TIgo3+dbX6EPHM904By3Z9XTBxsQfH6p1VoyUf0EGKZnUnJFezFtN9m2tyKbV/Z/5vCh9kVp6Yn/BE/AwGAH7kqqjPtHTnZiq+Xy1UwV4/eHkxGAvSAR3Z6wTQCt/rwqGrQi2eGpIcyjxTwlPYaVjfx3L+1tnBR966lGLnhwX0I6b6whXAm3hRb1AhYFnuyF/CoG/PEmQsMU5GfkroQkb5LL+UeCYKbjvMvgCe2hFxfh3dcGJ9E3dad9W0rSKrPd5t/dR1kDtItHau36+G9PSVyqRD1yt0MS2vLLUQu7t2RhiIPrl20fkbnum9JAfmLlgliHIiQPHASL32CXB6EzsgqRX6w8TmWNOSvlR7LU8JZtd4Gmiw9wGBh7JEGodkaH6lc5ndQluykC18RUXtuLft+S4dnQCApHX6FoIGZjug==",
      "username": "AgCgBQc/fhGqB0YBGfXzhybC6YXJeLkOZyi7Z7Y+HjfnYSg4Q/Zh8Kn7UEbq9CwEl+CtagARjKmLfhIcAqFWS8+h8j4A2xNq7gzLnv+eCo0vFDPTddDVvdb6ixmRvF5rzD1gZ2vxWzlWVqk7x0wt8wCE90S0yu40j+JOaqH35Ir3kb4NgTMXk6Yqlidw06r3P2cqbZ0jBleOFf5eRfiu0ZquU5PJ/J7t9Pecx9S8mlitTtFPlvpVprNPB+XPSz2uwcwNW9i5OBUgR3PXsOjILLog8SiWYyk7bHaWnJtZ+JVEi9isy4EiwyrDY5kHRK2kB9Nnf6a9zz2krP7W+w9a3qXJkv8GP9D2+FN9Pj+2WP4r0hz7JL0i5q9bcc5HgBKP946u87z2lEjv2ioUAghaG/zwol3q+tKv0i6pPe0guGRCdpMlXa1Z1deOBJvxJXanTrIwi7dVc/bCsRGMRyYwD6hWhe1JjxgBjc/YbbBj8JJVdHrc2tGYFBU9qG2Kv3cAZMRrMXvKUkTK8JiMVzN0/DHEtdNv1PW4U3hlAqt5b62WahyzdHNVqHycwe+Ogz0BfTdohlxftv5qQYx0SEynXaIY+WltRnCnYrY1Kg1/DmsWYCGy++TO+6cEEwISPe/FM1peidsXVf5S3DCUQWE6aMK/6XDzukZoTjor/8JPkHc56Pk1Paty0yrP+YdL5R5m3IERzHoD"
    }
  }
}

能夠看到內容已經獲得了加密,接下來就須要建立 SealedSecret 對象了:

$ kubectl -n test apply -f test-seal-secret.yaml

接着查看 SealedSecret 和 secret:

$ kubectl -n test get SealedSecret,secret
sealedsecret.bitnami.com/seal-test-secret   4s
secret/seal-test-secret      Opaque       3      3s

在以下的 Deployment 中以環境變量的形式引用上述生成的 secret:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: devops
  name: devops
spec:
  replicas: 1
  selector:
    matchLabels:
      app: devops
  template:
    metadata:
      labels:
        app: devops
    spec:
      restartPolicy: Always
      containers:
      - name: devops
        image: dllhb/devopsday:v0.6
        imagePullPolicy: Always
        envFrom:
          - secretRef:
              name: test-secret
        ports:
        - containerPort: 9999
          name: http
          protocol: TCP

使用下面命令部署:

$ kubectl -n test apply -f test-deploy.yaml
deployment.apps/devops created

查看 pod 狀態,並查看環境變量(secret 是以環境變量的形式注入 pod 內的):

$ kubectl -n test get pods
devops-b48df6659-gmjtr    1/1     Running   0          21s
$ kubectl -n test exec -it devops-b48df6659-gmjtr sh
env | grep -E "username|password|token"
username=xiaomage
token=1e4dgr5fgrh3rffgrhtonnhrhr
password=passw0rd

說明 secret 注入成功。其餘的 secret 類型,好比 tls、dockerconfigjson 等均可以用上面的方式進行使用。

最後就能夠將包含 secret 信息且通過加密的文件 test-seal-secret.yaml 推送至版本管理系統,好比 GitHub。

2、Helm Secrets

Helm Secrets 是 Helm 的一個插件,用來對於 Helm Chart 中的敏感信息進行加密。

2.1 原理

Helm Secrets Plugin 可使用多種加密方式來對敏感信息進行加解密(本文介紹 sops)。加密時使用helm secrets enc 命令對須要加密的文件內容進行加密;解密時helm secrets使用dec將加密內容進行解密,並添加在 values.yaml 文件中,後續的使用直接取用 values.yaml 文件中的值便可。

2.2 加密

image.png

使用helm secerts enc 對位於helm_vars目錄下的secrets文件加密時,helm secrets plugin會使用 public key 對內容進行加密(可看下文以 sops 爲例講述的 demo)。

2.3 解密

image.png

使用helm secrets install/upgrade命令對應用程序進行安裝或更新的過程當中,須要指定通過加密後的 secret 文件(-f 指定,一般位於 helm_vars 目錄下),helm secrets plugin 會選擇 private key 對加密的數據進行解密,解密後的數據在 values.yaml 文件中可找到,在 helm chart 的 template 目錄下的 secret 文件只須要引用相關的值便可(.Values.xxx)。

2.4 安裝

安裝的前提條件:

  • helm(>2.3)
  • sops

sops 是一個加密文件的編輯器,支持 YAML、JSON、ENV、INI 和二進制格式,並使用 AWS KMS、GCP KMS、Azure Key Vault 和 PGP 進行加密。

PGP(Pretty Good Privacy)是一種經常使用的加密方式。在 1990s(1991 年)由 Phil Zimmermann 所開發,如今歸屬於 Symantec 公司,它是商業軟件,須要付費才能使用。而 GPG(GNU Privacy Guard)是一種基於 Open PGP 標準的加密方式。它是開源且免費的。因此本文的演示將使用 GPG 的方式。

因爲sops採用非對稱加密,因此須要先生成一對key。使用gpg --full-generate-key並輸入必要的參數便可生成 key,以下:

$ gpg --full-generate-key
gpg (GnuPG) 2.2.12; Copyright (C) 2018 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 1y
Key expires at Sat Jan  8 12:12:10 2022 UTC
Is this correct? (y/N) y

GnuPG needs to construct a user ID to identify your key.

Real name: xiaomage
Email address: devops@xiaomage.com
Comment: gpg key generation
You selected this USER-ID:
    "xiaomage (gpg key generation) <devops@xiaomage.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: key 8BA2C5716B5C007F marked as ultimately trusted

gpg: revocation certificate stored as '/root/.gnupg/openpgp-revocs.d/BCEB5797691E6C95E33A465D8BA2C5716B5C007F.rev'
public and secret key created and signed.

pub   rsa4096 2021-01-08 [SC] [expires: 2022-01-08]
      BCEB5797691E6C95E33A465D8BA2C5716B5C007F
uid                      xiaomage (gpg key generation) <devops@xiaomage.com>
sub   rsa4096 2021-01-08 [E] [expires: 2022-01-08]

能夠查看生成的private keypublic key

  • private key 查看
gpg -K(gpg --list-secret-keys)

/root/.gnupg/pubring.kbx
------------------------
sec   rsa4096 2021-01-08 [SC] [expires: 2022-01-08]
      BCEB5797691E6C95E33A465D8BA2C5716B5C007F
uid           [ultimate] xiaomage (gpg key generation) <devops@xiaomage.com>
ssb   rsa4096 2021-01-08 [E] [expires: 2022-01-08]
  • public key 查看
gpg -k(gpg --list-keys)
gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   2  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 2u
gpg: next trustdb check due at 2022-01-08
/root/.gnupg/pubring.kbx
------------------------
pub   rsa4096 2021-01-08 [SC] [expires: 2022-01-08]
      BCEB5797691E6C95E33A465D8BA2C5716B5C007F
uid           [ultimate] xiaomage (gpg key generation) <devops@xiaomage.com>
sub   rsa4096 2021-01-08 [E] [expires: 2022-01-08]

在用 sops 加密數據以前,先建立一個.sops.yaml文件,寫一個加密的規則,好比只加密 key 是 username 和 password 的敏感信息,並且須要指定 gpg 的 fingerprint。

cat << EOF > .sops.yaml
creation_rules:
  - encrypted_regex: '^(username|password)$'
    pgp: 'B1C77B2CCF5575FAF0DA6B882CA51446C98C9D85'
EOF

接着,將下面的敏感信息寫入一個yaml文件:

cat << EOF > secrets.yaml
username: xiaomage
password: passw0rd
EOF

如今就能夠用 sops 來加密數據了:

$ sops -e secrets.yaml
username: ENC[AES256_GCM,data:s6pInMY3eGM=,iv:5Q7JsntVoKjseD3ApWcgmYeedmGXj2A1/PyGCNFHGdE=,tag:vInq3NBLxvVWXsoVUD46Rw==,type:str]
password: ENC[AES256_GCM,data:Ua7de2w6Jgw=,iv:qYIjTW1D0dh20NA8FGu4XEGI16kvYGAWIk4iu3r/Gdg=,tag:b33tpsP1vCgqlpyCEDP88Q==,type:str]
sops:
    kms: []
    gcp_kms: []
    azure_kv: []
    hc_vault: []
    lastmodified: '2021-02-06T12:08:57Z'
    mac: ENC[AES256_GCM,data:QHHDRSO2PyJt0/OA67ex0R39gEjWEnwg0MSnBac8QtLNh3ncY+9D8IZw/WqVnbcaiPta2Pem96yJZTZP4pum9ZX446iRKldsAXNqS4+tmlfowpMWI+1DgOa1QCkhSDH9U/2URA1dzyn3cZLPFzb5Ai6YUEQ93sRjlPI+kHXl16c=,iv:jhFM/uJSeChikUv777qgYVDFCHQhQeXlUSjiHx5X8Ow=,tag:6QTo5CsXQoqr0fK1B947ug==,type:str]
    pgp:
    -   created_at: '2021-02-06T12:08:51Z'
        enc: |
            -----BEGIN PGP MESSAGE-----

            hQIMA/AYjF0OZ4PLAQ/+LRc5vgpRhOez8q9up8t+3OVM5QdnMwSYiuwLvjfInqqk
            K19jUfUhwXDGGtSMlTotYlTWqWCiSm7sYeqFB0/Lx9lCZY5BhCrVnK7u7m8azpWU
            osCQNmJehflqqnBmn82nblOGnDjM/FYkcnz4+NHUPNyYV5tWjzw9s8i/WhDeuNrf
            IPnGKRCGJunWlHDP3yWMo7bnCNU/TmuRiSpf7lQLsp/U71M5t1X8RajatO7DPecq
            caq3VZ+Ynx0Qcgyt+aHugZw5Sw9oFOT4WVqwLlC/NKvrjtY8pCQ1HtY5/agLHrDw
            Hn2Phz1aQ+l4EAarphXCiYAFw/LHD2tisbQoApXe5tud9CjiyMu/14qhQalLgQxA
            yGcMmhEH7Ke4bubaA0ZPo8hBXAkxfdeicSzB/e1IkUP4LtlQiwPldDcDShB6MROH
            sK3RpELhSaNdfQZxqDVN0CgjRS0/AjboWejjrLQHD1hVcUDAU2WTyfvIaSxKpHIx
            ONo5sTvzYOjU/BRTLn0EujRP414xadOtt+4gEQDrGacYAokuiK2ev0dinHo32EWY
            j/vsb0o3whNRpBEGMZTUrl9HSkt58FQZsmu5JnL3ZYKiujHFoQS/aOcxD0slUxhC
            PoCnce6PgmB78RHOLHaXkTrORc+6oMpCGN8/K1hjXE+eH/kk4jv8yVLwmbg9XjLS
            XgFTcQYs6nVTSoWVea62kRN4qlC/XTJ6D91HXRX5UyB3qrZ3k+w9TOlM9quYYI/B
            E0FqbFVSKT3ekPQqF91a7tV01FIxpfr4Mvzy2+8xsXiAQtDm52PSlk9eovkAMqU=
            =nafU
            -----END PGP MESSAGE-----
        fp: B1C77B2CCF5575FAF0DA6B882CA51446C98C9D85
    encrypted_regex: ^(username|password)$
    version: 3.6.1

能夠看到 username 和 password 的值都被加密了。接下來就是使用這些加密內容了。

2.5 安裝使用 Helm Secrets plugin

執行如下命令安裝 helm-secrets plugin:

$ helm plugin install https://github.com/zendesk/helm-secrets

首先,建立一個演示用的 helm chart,並在根目錄下建立一個名爲helm_vars的目錄,用來存放用來加密的 secret,以下所示:

$ tree
.
└── devsecops
    ├── Chart.yaml
    ├── charts
    ├── helm_vars
    │   ├── secrets.yaml
    │   ├── tls.crt
    │   └── tls.key
    ├── templates
    │   ├── NOTES.txt
    │   ├── _helpers.tpl
    │   ├── deployment.yaml
    │   ├── hpa.yaml
    │   ├── ingress.yaml
    │   ├── secrets.yaml
    │   ├── service.yaml
    │   ├── serviceaccount.yaml
    │   └── tests
    │       └── test-connection.yaml
    └── values.yaml

將須要加密的信息寫入secrets.yaml文件裏面,好比:

cat << EOF > secrets.yaml
secret_data:
  username: xiaomage
  password: passw0rd
EOF

使用 helm secrets 來加密上述文件:

$ helm secrets enc secrets.test.yaml
Encrypting secrets.test.yaml
Encrypted secrets.test.yaml

查看加密後的文件內容:

secret_data:
    username: ENC[AES256_GCM,data:O/1pyNsL3Gc=,iv:HZ0MrGWaBxM37cIkp/JdsA5gRzw6aJFfBR19rno3h5I=,tag:2SiMs46lonnwECc8RHfT/Q==,type:str]
    password: ENC[AES256_GCM,data:l15XlhZ4CsM=,iv:TMbV6+Rh2wGpMlHi7zJsHWM6IxMK2hBuMKsD82p8LiY=,tag:N4Kbftl//B1U2R9Khsduzg==,type:str]
sops:
    kms: []
    gcp_kms: []
    azure_kv: []
    hc_vault: []
    lastmodified: '2021-02-07T06:19:15Z'
    mac: ENC[AES256_GCM,data:dSXjEbKyBXVtqqSqshGXKUwDJcMVZrDf2GxFj0Oor3FDnNeS+bTY4Yubv1J0XlzU6yxO0Y87NzVN84unkF/Ph95JJV2opk6a0VTtaxKYOFUVneyY5WQ2glHEntX+aEq1lJkW1Sd34i/tvWeSABemIX4M2xcIOdIaCHgzk//vi9w=,iv:febius/ashzpdfKStJnQYVG/3FrVaYw102q87P9+egQ=,tag:/MUXrxhhOk6F8MS5wi7cLQ==,type:str]
    pgp:
    -   created_at: '2021-02-07T06:19:08Z'
        enc: |
            -----BEGIN PGP MESSAGE-----

            hQIMA7Oc9Dk1ccccARAAk7l23omTBRThnP7YC5AHdqzEO8Lapxc8ycWg5tsbM8eE
            JaRFn4u3/+dQdpL6xlHv1wu0kmrZUgG8P41WmNDIKb2GtAlHQk+bjjV2IU0lCEj7
            9UZXuAyhxHtVjHMBnzjppFh+6L0nH2K5AGaJWATwhO9M6CqmdCFnWJx7vAPfVQZF
            Li9zqHK/YsbwgEWKs0bVvJ1btB7u4J5olKagYaZhaFaLzwjbtXmEqDUpfmPkooNr
            7kPSVe8IMv/+MUaJY6uYNTBGWGrije4bY4A+hA/dUj4yN0gqqd796oc9GuN1MJSO
            cAAoiTW2Vrw3OdyP7PIJVuxlS9gXnxtBOjo+p/Ij91ELq+DnC+6bGS9UIeF+Y1RD
            h4siwx7I7hzk9tp+tXmsfdJit+usK6raPzYkcBgZVF8woKZsp2/qxloYyIFJ0sbK
            MO67+dcAg+AX0M0/u33t1BAMTt/LJ1V2ZQUl+yzjRSKfZ2bCmd/skkE3VZx2ls44
            LMngWZG7EzE39Onw9PB3ukXD7W+X+BThc2AJzVotrpDWbSI2/anoM9TMJjYfBjyU
            xBuTuoviT5ENdm14bGomww9G+Ean3dyC2vWoHhY2KfuPlSxZ6mDIDm5zAPkZZl5A
            QHjtaPT5qymPCpqy2X3yvK76zyJhfWYFIHguOy3JlDxiONC9DH1M6OVWoC69pPzS
            XgEtII9fTeLXFU5Jy9gJa5nNKEQY87OkSXl3TFAiQ9OmgDbuUHZuvQzlecsKwR2s
            mS7P7Z3Bb+eRakQ41Gzw4B7wmOrm2w0t4guVJDNIP/gQB0XBO1XZj4RsbMKn070=
            =yQ2R
            -----END PGP MESSAGE-----
        fp: B1C77B2CCF5575FAF0DA6B882CA51446C98C9D85
    encrypted_regex: ^(data|username|password|.dockerconfigjson|token|token1|key|crt)$
    version: 3.6.1

須要注意的是,此時只是加密了須要加密的內容,可是這些內容該怎麼用呢?其實也比較簡單,就是:正經常使用。舉例來講,在 helm chart 中,正經常使用 secret 的方式以下:

apiVersion: v1
kind: Secret
metadata:
  name: test
  labels:
    app: devsecops
type: Opaque
data:
  {{- range $key, $value := .Values.secret_data}}
  {{ $key }} : {{ $value | b64enc | quote}}
  {{- end}}

而上面循環中引用的值.Values.secret_data就是來自於上面helm_vars目錄下的加密文件。

怎麼作到的呢?簡單點說,就是執行 helm 的相關命令(install 或者 upgrade)時,會利用 sops 的 private key 將 helm\_vars 目錄下的加密內容解密,而且「存放在」values.yaml 文件中,接下來的就和正常的 helm chart 使用是同樣的了。在 chart中的 deployment.yaml 文件中引用 secret:

......
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          envFrom:
            - secretRef:
                name: test
......

接下來可使用下面命令將前文的 chart 進行安裝:

$ helm secrets install test . --namespace test -f helm_vars/secrets.yaml -f values.yaml
須要注意的是,在上面的命令中,必須指定helm_vars/secrets.yaml文件。
接着查看生成的pod、secret:
$ kubectl -n test get pods,secret
pod/test-devsecops-7876ffc8b7-967xr   1/1     Running   0          6s
secret/test      Opaque             2      8s

因爲上面的 secret 是以環境變量的形式注入到 pod 裏面的,能夠查看進行驗證:

$ kubectl -n test exec -it test-devsecops-7876ffc8b7-967xr sh
$ env | grep -E 'username|password'
username=xiaomage
password=passw0rd

能夠看到 secret 解密成功,併成功注入 pod。

最後就能夠將加密後的文件上傳至源碼管理系統了(好比用 git push 至 GitHub)。

3、Kamus

Kamus 是一款開源的採用零信任的 secret 加解密方式爲 Kubernetes 應用程序安全處理 secrets 的工具。Kamus 提供兩種方式來對 Kubernetes secrets 進行加密,即

  • 使用 init container:將 secrets 加密後存儲爲 Kubernetes configmap,而後在應用程序的部署中添加一個 init container,經過 init container 將 configmap 中的加密數據解密至指定文件,應用程序再今後文件讀取解密後的 secret 信息。
  • 使用 KamusSecret:建立一個 KamusSecret 對象(充分利用了 Kubernetes 的擴展機制),此對象包含了加密以後的數據,在此對象建立的過程當中,會建立一個同名的 secret 對象,secret 中的數據是通過 base64 編碼以後的數據,能夠被 Kubernetes 其餘對象引用。

3.1 原理

Kamus 一樣是使用客戶端工具 kamus-cli 對於須要加密的數據進行加密(區別於 Sealed Secrets 的是,Kamus 的數據須要單獨逐一加密,而不是所有存放在 secret 文件裏面一次性加密,下面會看到),下面依舊以加解密的方式分別闡述。

3.2 加密

image.png

用 kamus-cli 對須要加密的數據逐一加密時,位於集羣上的 controller 會選擇 public key 並使用 kamus encryptor 對數據進行加密,隨後將加密內容保存在 configmap 中(以 init container 的方式使用)或者 KamusSecret 中(以 secret 的方式使用)。

3.3 解密

image.png

當將加密後的內容進行部署時(kubectl apply/create),位於集羣上的 controller 會選擇 private key,並使用 kamus decryptor 對於數據進行解密,若是是使用 KamusSecret 存儲的數據,則 controller 會將生成一個與 KamusSecret 對象同名的 Secret 對象,此 Secret 中存放由通過 base64 編碼後的信息;若是是使用 configmap 的形式,則此 configmap 會以 volume 的形式掛載到 pod 內,隨後在 pod 中使用 init container 來調用 kamus-decryptor 的 api 將加密信息解密,並存放到指定的文件中,隨後 pod 內的應用程序能夠經過讀取此文件內容來獲取敏感信息。

KamusSecret 和 Secret 二者的關係與 Deployment 和 Pod 之間的關係相似。

3.4 安裝

kamus 的安裝包括 controller 和 客戶端工具 kamus-cli。

  • 安裝 controller
$ helm repo add soluto https://charts.soluto.io
$ helm install kamus --namespace kamus soluto/kamus

檢查 pod 狀態:

$ kubectl -n kamus get pods
NAME                                READY   STATUS    RESTARTS   AGE
kamus-controller-55d959895d-hdklf   1/1     Running   0          9m30s
kamus-decryptor-5974b6ff47-5pkbr    1/1     Running   0          7m34s
kamus-decryptor-5974b6ff47-c4jt4    1/1     Running   0          7m34s
kamus-encryptor-f75dd457-fwp8r      1/1     Running   0          9m28s
kamus-encryptor-f75dd457-p9rnx      1/1     Running   0          9m29s
  • 安裝客戶端
$ npm install -g @soluto-asurion/kamus-cli

檢查安裝是否成功:

$ kamus-cli -V
0.3.0

3.5 使用

下面分別以init container和 KamusSecret 的方式來演示使用方式。

1)以init container的方式

首先須要加密 secret。kamus 加密 secret 時須要和一個 service account 關聯起來,此 service account 會在後續的應用部署中使用,因此先建立一個 service account(本文全部 demo 均在 test ns 下):

$ kubectl -n test create sa xiaomage

接着使用客戶端工具 kamus-cli 來加密 secret(username 的值爲 xiaomge,password 的值爲 passw0rd):

$ kamus-cli encrypt --secret xiaomage --service-account xiaomage --namespace test --kamus-url http://localhost:9999 --allow-insecure-url
[info  kamus-cli]: Encryption started...
[info  kamus-cli]: service account: xiaomage
[info  kamus-cli]: namespace: test
[warn  kamus-cli]: Auth options were not provided, will try to encrypt without authentication to kamus
[info  kamus-cli]: Successfully encrypted data to xiaomage service account in test namespace
[info  kamus-cli]: Encrypted data:
CxujUBK4jgdjt+wP5mXyDA==:+CoXRDJVtW3EZ2FpterVTA==

返回的CxujUBK4jgdjt+wP5mXyDA==:+CoXRDJVtW3EZ2FpterVTA== 就是 xiaomage 這個值通過加密後的值,用一樣的方法,能夠將 passw0rd 進行加密:

$ kamus-cli encrypt --secret passw0rd --service-account xiaomage --namespace test --kamus-url http://localhost:9999 --allow-insecure-url
[info  kamus-cli]: Encryption started...
[info  kamus-cli]: service account: xiaomage
[info  kamus-cli]: namespace: test
[warn  kamus-cli]: Auth options were not provided, will try to encrypt without authentication to kamus
[info  kamus-cli]: Successfully encrypted data to xiaomage service account in test namespace
[info  kamus-cli]: Encrypted data:
ChmNEPM8Nj7Huh1YwO5xOA==:r9MHhEyTIEaQ4hw837lA9w==

返回的ChmNEPM8Nj7Huh1YwO5xOA==:r9MHhEyTIEaQ4hw837lA9w== 就是 passw0rd 這個值通過加密後的值,將上述兩個加密後的值放在 configmap 中:

apiVersion: v1
kind: ConfigMap
metadata:
  name: kamus-encrypted-secrets-cm
  namespace: test
data:
  username: CxujUBK4jgdjt+wP5mXyDA==:+CoXRDJVtW3EZ2FpterVTA==
  password: ChmNEPM8Nj7Huh1YwO5xOA==:r9MHhEyTIEaQ4hw837lA9w==

接下來,將上述 configmap 以 volume 的方式掛在到 pod 中,隨後使用 init container 來解密數據,且將數據存放在一個 config.json 文件中:

apiVersion: v1
kind: Pod
metadata:
  namespace: test
  name: kamus-pod
spec:
   serviceAccountName: xiaomage
   automountServiceAccountToken: true
   initContainers:
   - name: "kamus-init"
     image: "soluto/kamus-init-container:latest"
     imagePullPolicy: IfNotPresent
     env:
     - name: KAMUS_URL
       value: http://kamus-decryptor.kamus.svc.cluster.local/
     volumeMounts:
     - name: encrypted-secrets
       mountPath: /encrypted-secrets
     - name: decrypted-secrets
       mountPath: /decrypted-secrets
     args: ["-e","/encrypted-secrets","-d","/decrypted-secrets", "-n", "config.json"]
   containers:
   - name: kamus-test
     image: dllhb/devopsday:v0.6
     imagePullPolicy: IfNotPresent
     volumeMounts:
     - name: decrypted-secrets
       mountPath: /secrets
   volumes:
   - name: encrypted-secrets
     configMap:
       name: kamus-encrypted-secrets-cm
   - name: decrypted-secrets
     emptyDir:
       medium: Memory
須要注意的是,須要指定 kamus 的地址,即 decryptor 的地址,可根據本身的安裝狀況自行指定。

接下來,部署 configmap 和 pod 並查看:

$ kubectl -n test apply -f configmap.yaml
$ kubectl -n test apply -f kamus-deploy.yaml
$ kubectl -n test get pods,cm
NAME                          READY   STATUS    RESTARTS   AGE
pod/kamus-pods              1/1     Running   0          4h3m

NAME                                   DATA   AGE
configmap/kamus-encrypted-secrets-cm   2      30s

進入 pod 查看解密後的數據:

$kubectl -n test exec -it kamus-deploy sh
$ cat /secrets/config.json
{
    "password":"passw0rd",
    "username":"username"
}

能夠看到 secet 已經被解密到了 config.json文件中,應用程序只須要讀取此文件便可得到 secret 的相關數據。

2)以 KamusSecret 的方式

kamus 對 Kubernetes 進行了擴展,有了本身支持的 KamusSecret 對象,將上述加密後的數據存放在 KamusSecret 中:

apiVersion: "soluto.com/v1alpha2"
kind: KamusSecret
metadata:
  name: kamus-test
  namespace: test
type: Opaque
stringData:
  username: CxujUBK4jgdjt+wP5mXyDA==:+CoXRDJVtW3EZ2FpterVTA==
  password: ChmNEPM8Nj7Huh1YwO5xOA==:r9MHhEyTIEaQ4hw837lA9w==
serviceAccount: xiaomage

建立 KamusSecret 對象:

$ kubectl -n test apply -f kamus-secrets.yaml

查看生成的 KamusSecret 和 Secret:

$ kubectl -n test get KamusSecret,secret
NAME                                AGE
kamussecret.soluto.com/kamus-test   60s

NAME                                TYPE                                  DATA   AGE
secret/kamus-test                   Opaque                                2      59s

能夠看到 KamusSecret 生成了一個和本身同名的 secret,接着查看 secret 的內容:

apiVersion: v1
data:
  password: cGFzc3cwcmQ=
  username: eGlhb21hZ2U=

解碼後爲:

password: passw0rd
username: xiaomage

此時,能夠像正常方式在 pod 中引用此 secret(像前文的 Sealed Secret 章節所演示的同樣,再次再也不贅述)。

最後就能夠將加密後的文件上傳至源碼管理系統了(好比 git push 至 GitHub)。

寫在最後

其實,安全處理 Kubernetes secret 的方式不只僅上面的三種形式,還能夠利用諸如 vault 等來管理應用程序部署中的敏感信息。可是不一樣的工具、不一樣的方式,其背後的思想和思路都差不太多。

總結起來,差很少有如下幾個點:

  • 充分利用 Kubernetes 的擴展能力,自定義一個 secret 對象用來存儲加密後的數據(諸如 Sealed Secrets 的 SealedSecret 對象;Kamus 的 KamusSecret),讓這些擴展的對象生成對應的 secret 對象,再正常使用 secret 對象。
  • 利用外部的敏感信息管理工具(諸如 vault 或者其餘雲廠商提供的服務)。
  • sops 是一種使用方便的加密工具,也是上述工具實現加密的祕密武器。

沒有一勞永逸的安全,只有永不止步的行動。任何改變都是重要的。

參考

https://github.com/bitnami-la...

https://github.com/Soluto/kamus

https://kamus.soluto.io/

https://blog.solutotlv.com/ca...

https://en.sokube.ch/post/lig...

https://github.com/mozilla/so...

來源:DevSecOps SIG

做者:小馬哥

4月每週四晚8點,【冬哥有話說】DevOps之庖丁解牛,拆解DevOps的工具及具體實戰。公衆號留言「解牛」可獲取地址

  • 0401《數據庫持續交付流水線分享與演示(Azure DevOps+Flyway)》
  • 0408《持續交付中的版本管理與基於Azure DevOps擴展框架的插件開發》
  • 0415《微服務,多團隊協做中的API測試怎麼作 - Pact契約測試》
  • 0422《BoatHouse端到端流水線展現》
相關文章
相關標籤/搜索