เข้ารหัส Kubernetes Secret ด้วย SOPS และ Azure Key Vault

ถ้าหากใครเคยใช้งาน Secret object บน Kubernetes ก็จะคุ้นเคยกับการสร้าง secret เป็นอย่างดี โดยเราสามารถสร้าง Secret ได้ทั้งจาก kubectl command หรือผ่านไฟล์ YAML โดยเมื่อเราสร้าง secret บน cluster ได้แล้วเนี่ย ปัญหาถัดมาก็คือว่า แล้วเราจะจัดเก็บหรือทำ versioning ของตัว secret ของเราได้อย่างไรบ้าง เพราะการจะนำ secret ที่เต็มไปด้วยข้อมูลที่ควรจะเป็นความลับไปเปิดเผยอยู่ใน public git repository ก็คงจะไม่ใช่เรื่องที่ดีนัก

ทางแก้ที่ผมได้ลองนำมาใช้ก็คือการเข้ารหัสข้อมูลด้วย SOPS และทำการปล่อยให้ไฟล์ที่ผ่านการเข้ารหัสแล้ว อยู่ใน git repository ไปเลย เนื่องจากว่าเราได้ทำการเข้ารหัสข้อมูลสำคัญไปเรียบร้อยแล้ว โดยใช้ควบคู่กับ Azure Key Vault เราก็จะสามารถจัดการ key ได้อย่างง่ายดายมากขึ้นอีกด้วย

SOPS

SOPS เป็น tool ที่พัฒนาโดย Mozilla ซึ่งทำหน้าที่ในการเข้ารหัสข้อมูลที่อยู่ในรูปแบบ YAML, JSON, และอื่นๆ โดยกุญแจในการเข้ารหัสนั้น รองรับแทบจะทุก service ที่มีให้บริการ ไม่ว่าจะเป็น GCP KMS, Azure Key Vault, หรือ PGP Key

username: sethanant
password: 12345678
id:
- thetkpark
- sethanantp
YAML ก่อนที่จะเข้ารหัสด้วย SOPS

อย่างเช่นถ้าเรามีไฟล์ YAML หน้าตาแบบด้านบน ถ้าเราเข้ารหัสด้วย PGP Key เราก็จะได้ไฟล์ที่เข้ารหัสแล้วออกมาเป็นแบบนี้

username: ENC[AES256_GCM,data:D2VbxrVI8As/,iv:ppre2lRUKOXEF1JOZYrC6mmZgRskS29qiIJrGS7vZPE=,tag:reGY8Xh8dKdz8i8DfUnpEA==,type:str]
password: ENC[AES256_GCM,data:JjeXWeJ7p74=,iv:d0I+TVFefWuczke0qChfI5BTfoIN9jydsAbZy3W3Dd8=,tag:4r8WUwLbwr7JrcvcEbOxYQ==,type:int]
id:
    - ENC[AES256_GCM,data:1lTfThlhv1wn,iv:bjy9mAWq941bZ2+8fkY04un2gOV6CDVwHeUnIAMLUug=,tag:ol/lxvh06FGtTRa6R18Ghw==,type:str]
    - ENC[AES256_GCM,data:gbltQVKaShFOSw==,iv:ni7ZLpswv/0Jax/70dhakfol3TSvNU1MSZmpF+iuwUE=,tag:yhYD+q5XpfiXAuigO3TtIA==,type:str]
sops:
    kms: []
    gcp_kms: []
    azure_kv: []
    hc_vault: []
    age: []
    lastmodified: "2021-10-07T14:35:37Z"
    mac: ENC[AES256_GCM,data:dqmRye6j+sGgiXlGE9MA3J6JOojPvMYDK4GfVAGCtZqmLwGWQ1CadRCM+fsElvNQIIP+Yw103FZEBLUC4DiSssZ5ctnc5XcYba8h8tosdJoCZlHcZwUlVcxdO6e5iykmmQBDAG25beatLt9D/wGC5/UrWmsmWmcaTrA4ZjMlK5E=,iv:+e+Idc8m/wBst0ga49oyNxCg6nP3hdvnzoJat+4Ojac=,tag:fAO1tKHhAt6dY93iEFT1zA==,type:str]
    pgp:
        - created_at: "2021-10-07T14:35:36Z"
          enc: |
            -----BEGIN PGP MESSAGE-----
            hQIMAwjUFcWUnUdiAQ//VOlyWmFKOi6VciNV27irut4Bo3vbVDzVL5Rn8l9OrQEC
            TeCycc9nuoHFFxUZmfBoy6cWdIeS5omsLEQrVNyPonwtGuOnGqa43BhiltgwprKe
            yXjGcHgcDWiLeWWe4xAfrdBTOXMcRYXeYa1HI427qHmFlfg3RhdV74WEnh/kQS5m
            E1O1NbEHPAMjV2DlL3q2rDg/dnOdVngKRGtpttadtPyPVlQcfb39PD4pUsvJ2BhF
            kdoVSFW0u8Rp39LQa1SrH1+4CsijGmmr87tbeeKa9ERFj56QDS4pgb6nC9YIAXB/
            /MkObKMuusEzTbcT+p/DKE7ebAgbxNP46zm1mO35yhejX3342vdxiG0eW7v88hHy
            gyuXSKVkKCPExKWGws1s30qaKvSnRHT3i8RCOjCIbgHu7ZbmY2xKx+1j5xDiW3ZG
            YXFUdG6CvmQpbs5NWc7eJedQpQ0CZbLiJxQ/NiJ84aFk69UniMgV8Zac3LTfv/nf
            w6HWEScwAJaX0UMGTszw9zPiAa2YhEnbMOXiif9Omt3SOfYu1lGnBKDRd7Nkb0X1
            AN5G2Azq6rcE5SH9Kh7TUQUOO2slpqtoszhHlKi60RuDWXUVOUZrryukRShi1Q60
            LeLA2oAut1yYrfMuMIFh0OpNkseQ+iYlZFYJCBILCnXXltp8ILfZr9qmpJ22uffS
            XAECK5nMBPqC/g5zlmVVKG1FqxzxrGnb96pOkL3H805k5458JhVT2qkcS8xqTVJj
            nYo4IcJP5Be2+b0HHkf3KW66yGCTkdiRlgte4f6mgrdQvdFFuzl3IDY6iwq4
            =NtkO
            -----END PGP MESSAGE-----
          fp: D5E9B63045A763DE06CBF7622A9C60164D676E91
    unencrypted_suffix: _unencrypted
    version: 3.7.1
YAML หลังเข้ารหัสด้วย SOPS

เราจะเห็นได้ว่าทุกๆ value ที่เราเขียนไว้ใน YAML นั้นถูกเข้ารหัสไว้ และมีการเพิ่มส่วนของ Metadata ของ SOPS เข้ามา โดย SOPS จะใช้ส่วนนี้ในการถอดรหัสต่อไป

Azure Key Vault

Key Vault เป็นบริการการจัดการ key ของ Azure ที่จะช่วยเราในการสร้าง จัดเก็บ และเรียกใช้ key ของเรา โดยข้อดีข้องการใช้ Managed Key ก็คือเราไม่ต้องดูแล key นั้นด้วยตัวเอง หน้าที่ของเรามีแค่การสร้างและเรียกใช้เท่านั้น

ดังนั้นเราจึงไม่ต้องกังวลว่า key ของเราที่เก็บไว้ในเครื่องนั้นจะถูก hack หรือขโมยเนื่องจาก key ของเรานั้นถูกเก็บรักษาอยู่บน Azure ซึ่งมีมาตรฐานความปลอดภัยกำกับไว้ เพราะฉะนั้นเครื่องคอมพิวเตอร์ของเราก็จะไม่ต้องรับหน้าที่ในการเก็บข้อมูลสำคัญ โดยไม่ว่าเราจะเปลี่ยนเครื่องคอมหรือว่าข้อมูลทั้งหมดหายไป ขอแค่เรายังเข้ายัง Azure ได้ key ของเราก็จะยังคงอยู่

Let's Get Started!

Download and Install

ขั้นแรก เราต้อง Download SOPS และติดตั้งลงในเครื่องเราก่อน โดยสามารถ Download ได้จาก https://github.com/mozilla/sops/releases และทำการเลือก OS ที่ใช้งานอยู่

เมื่อ Download มาแล้วเราจะได้ไฟล์ executable มาโดยเราสามารถเรียกใช้งาน SOPS ผ่านไฟล์นั้นได้เลย หรือว่าจะนำไปเพิ่มใน PATH environment variable ก็ได้

เพื่อเช็คว่าทุกอย่างเรียบร้อยดี ให้ลองรัน SOPS -v เพื่อทำการเช็คว่าเราสามารถใช้คำสั่ง SOPS ได้

นอกจาก SOPS แล้ว เรายังต้องทำการติดตั้ง Azure CLI เนื่องจากว่า ในการใช้ key ที่อยู่บน Azure Key Vault เราต้องทำการ Authenticate กับทาง Azure เพื่อให้ SOPS เข้าถึง key นั้นได้ โดยลำดับของการเลือก authenticate กับ Azure ของ SOPS จะเป็นดังนี้

  1. Client credentials
  2. Client Certificate
  3. Username Password
  4. MSI
  5. Azure CLI auth

ในบทความนี้ผมจะเลือกใช้ Azure CLI โดยสามารถดูวิธีการติดตั้งได้จาก https://docs.microsoft.com/en-us/cli/azure/install-azure-cli โดยให้ทำการรันคำสั่ง az login เพื่อเข้าสู่ระบบให้กับ Azure CLI ของเราให้เป็นที่เรียบร้อย

Setting up Azure Key Vault

ขั้นแรกให้เราเข้าไปที่ http://portal.azure.com และเข้าไปที่ Key Vault ผ่านแถบค้นหา หลังจากนั้นให้กด Create เพื่อทำการสร้าง Key ใหม่ขึ้นมา

ในแถบ Basic ให้ทำการเลือก Resource Group ที่ต้องการ และตั้งชื่อให้กับ Key Vault (ชื่อของ Key Vault ต้อง globally unique) โดยเราสามารถเลือก Region และ Pricing Tier ได้ โดยผมจะเลือกเป็น Standard Pricing กับ SEA Region (Option นอกเหนือจากนี้ก็สามารถไปอ่านได้จาก Documentation นะครับ)

ในแถบ Access Policy ให้กดที่ Key Permission และทำการเลือก Decrypt และ Encrypt เพิ่มเข้าไปด้วย เพื่อให้เราสามารถที่จะใช้ Key ในการ Encrypt และ Decrypt ได้

หลังจากนั้นก็เลือก Review + Create เพื่อทำการสร้าง Key Vault ของเราขึ้นมา

โดยเมื่อ Azure ทำการสร้าง Key Vault เรียบร้อยแล้ว ขั้นตอนต่อไปคือการสร้าง Key เพื่อใช้งานกับ SOPS โดยให้เข้าไปที่ Key Vault ที่เราสร้างขึ้นมา และเลือกที่แถบ Keys ใน Settings Section และกดที่ Generate/Import

โดยผมจะเลือก Generate key ขึ้นมาใหม่ โดยตั้งชื่อเป็น sops-test-key และใช้ RSA ขนาด 2048 หลังจากนั้นก็กด Create ได้เลย

เมื่อเราสร้าง Key เรียบร้อยแล้ว ให้เข้าไปใน key นั้นและเลือก current version โดยเมื่อเข้าไป เราจะเจอกับ Key Identifier ให้เรา Copy ตัวนี้ไว้ เพื่อใช้กับ SOPS ต่อไป

Encrypt the YAML

ไฟล์ YAML ตัวอย่างที่ผมจะใช้เป็น declaration ของ Kubernetes Secret หน้าตาแบบนี้นะครับ

apiVersion: v1
kind: Secret
metadata:
  name: my-secret
type: Opaque
data:
  PASSWORD: superStrongPassword
  API_KEY: someAPIKey
  PG_PASSWORD: idolm@ster
Kubernetes Secret YAML ตั้งต้น

และเนื่องจากว่า value ของ secret ใน Kubernetes นั้นต้องเข้ารหัสอยู่ในรูปแบบ Base64 ผมก็จะใช้ K64 (แอบขายของ) แปลงออกมาเป็นตามนี้ครับ

apiVersion: v1
kind: Secret
metadata:
  name: my-secret
type: Opaque
data:
  API_KEY: c29tZUFQSUtleQ==
  PASSWORD: c3VwZXJTdHJvbmdQYXNzd29yZA==
  PG_PASSWORD: aWRvbG1Ac3Rlcg==
Kubernetes Secret YAML ที่แปลง value เป็น Base64 แล้ว

หลังจากนั้น เราก็จะสามารถใช้คำสั่ง SOPS เพื่อเข้ารหัสข้อมูลของเราได้ตามคำสั่งนี้

$ sops --encrypt --azure-kv https://sops-key-test.vault.azure.net/keys/sops-test-key/25053b6b49074e83b00a42b22ffa897a secret.yaml
คำสั่งในการเข้ารหัสไฟล์ secret.yaml ด้วย SOPS และ Azure Key Vault
apiVersion: ENC[AES256_GCM,data:pqM=,iv:FB3sNZIrJykdJoxUTYZXYWDug6YR36UcQWyJ1NGTpS8=,tag:74j7b0QDhCgWALdcBj2XWA==,type:str]
kind: ENC[AES256_GCM,data:rBok34Ff,iv:LbW0iLeHslWywRmfBlK2z4ZvOlxYvNXOt2a3biSt3NY=,tag:Nzwy5cakYk3RIWpC6u9YXQ==,type:str]
metadata:
    name: ENC[AES256_GCM,data:W3+/wWHlr5El,iv:43EJTT4Cpdkec769sYngQ63xungGBEJCkGl3pm+BPgw=,tag:sa1xNQdFXs0zufbs7lWpRg==,type:str]
type: ENC[AES256_GCM,data:2WTqeQCl,iv:WpFOl/qLbsnutMuK9TrDK40XH72Z76vmqL+rK169Usg=,tag:fTa/jAT3lZ6+8TBj5Rkybw==,type:str]
data:
    API_KEY: ENC[AES256_GCM,data:lex3jARP6o8wATQoeaZlsg==,iv:Ny7kl9yNWSy3ROUkUd6VPYom2YVOli5yi7YlC9Fg9UY=,tag:3tcR8nYZW/+Jkhy2j/XS4A==,type:str]
    PASSWORD: ENC[AES256_GCM,data:en/ZKgZqmNM84i4JaXVwPUwbT+7VDa78Dp07DQ==,iv:Ta3IFSEdmDAwn32NToi3PIrhGiYjUt37z1BYenIfRB0=,tag:sF9F5KcuE+0mAHkGqhjyow==,type:str]
    PG_PASSWORD: ENC[AES256_GCM,data:vV6YMjBsLXtUo5jKrBWDEA==,iv:2z2wwrDcY4g/jMukTeUD3mpEe+QbL6ZeGUDrtx92z0k=,tag:A5EX+cvh0tSlgSSUlvD+hQ==,type:str]
sops:
  ...
Kubernetes Secret YAML ที่ถูกเข้ารหัสด้วย SOPS และ Azure Key Vault

โดยเราจะได้ไฟล์ที่เข้ารหัสแล้ว ถูกแสดงผลออกมาทาง CLI ซึ่งเราสามารถทำการเซฟลงไฟล์ได้ดังนี้

$ sops --encrypt --azure-kv https://sops-key-test.vault.azure.net/keys/sops-test-key/25053b6b49074e83b00a42b22ffa897a secret.yaml > secret.enc.yaml
คำสั่งในการเข้ารหัสไฟล์ secret.yaml และเขียนไฟล์ลง secret.enc.yaml

ในไฟล์ที่ถูกเข้ารหัส ทุกๆ key ใน YAML ของเรานั้นจะถูกเข้ารหัสทั้งหมด ซึ่งบางครั้งเราอาจจะต้องการเข้ารหัสแค่ Key Value ที่เป็นความลับเท่านั้น ในกรณีนี้สิ่งที่เราไม่ต้องการให้คนอื่นรู้ก็มีเพียงแค่ Key Value ใน data เท่านั้น

ซึ่ง SOPS สามารถกำหนด Regular Expression ได้ เพื่อให้ SOPS เข้ารหัสเฉพาะสิ่งที่เราต้องการ

# กำหนดได้ผ่าน --encrypted-regex
$ sops --encrypt --encrypted-regex '^(data|stringData)$' --azure-kv https://sops-key-test.vault.azure.net/keys/sops-test-key/25053b6b49074e83b00a42b22ffa897a secret.yaml > secret.enc.yaml
คำสั่งในการเข้ารหัสไฟล์ secret.yaml ด้วย RegEx เขียนไฟล์ลง secret.enc.yaml

ไฟล์ที่ได้ออกมาก็จะมีแค่ Value ของ Key ที่อยู่ใน data เท่านั้นที่ถูกเข้ารหัส

apiVersion: v1
kind: Secret
metadata:
    name: my-secret
type: Opaque
data:
    API_KEY: ENC[AES256_GCM,data:DMD9xCD9P5q7uJsqzDBM3g==,iv:+v2uESI57WJUQh8kIDeE1VP/aD20gCAzxGN1vjig2Cg=,tag:7Ct+0pcGwOlth0vM1wjRVg==,type:str]
    PASSWORD: ENC[AES256_GCM,data:f9S3B3jPoBUl02Z7OL89U2mbOGT+M10x0ndqNw==,iv:TW4qEa18WZUoCUHXP0yW1wbv+FT1EsK+bWiiuQlugSU=,tag:Mj2Z98ibDPzItBPndsQCqw==,type:str]
    PG_PASSWORD: ENC[AES256_GCM,data:kjXZ0eXpK+T1S877Fh+gGA==,iv:iAWKy5o2FdPbM06ym94gxL6Ea0rNAW55UQ4OFg5CSSQ=,tag:07XCCcQDCPc/iPcPKnWdCQ==,type:str]
sops:
    ...
Kubernetes Secret YAML ที่ถูกเข้ารหัสด้วย SOPS ที่มีการกำหนด RegEx

Decrypt the YAML

คำสั่งที่ใช้ในการถอดรหัสก็ง่ายมากๆ เพราะข้อมูลทุกอย่างที่ SOPS ต้องการจะใช้นั้น ถูกเก็บอยู่ในไฟล์ที่ถูกเข้ารหัสไว้อยู่แล้ว

# แสดงผลออกทาง CLI
$ sops --decrypt secret.enc.yaml

# ในกรณีที่ต้องการเซฟข้อมูลที่ถอดรหัสแล้วลงในไฟล์
$ sops --decrypt secret.enc.yaml > secret.yaml
คำสั่งในการถอดรหัสไฟล์ secret.enc.yaml

That's It!!

เท่านี้แหละครับ สำหรับการใช้ SOPS และ Azure Key Vault เข้ารหัส Kubernetes Secret ของเรา การทำแบบนี้จะทำให้เราสามารถเก็บตัว Secret declaration ไว้ใน version control ที่เป็นสาธารณะได้ โดยไม่ต้องกลัวเรื่องการจัดการ key สักนิดเลยครับ