เข้ารหัส 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 หน้าตาแบบด้านบน ถ้าเราเข้ารหัสด้วย 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
เราจะเห็นได้ว่าทุกๆ 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 จะเป็นดังนี้
- Client credentials
- Client Certificate
- Username Password
- MSI
- 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
และเนื่องจากว่า value ของ secret ใน Kubernetes นั้นต้องเข้ารหัสอยู่ในรูปแบบ Base64 ผมก็จะใช้ K64 (แอบขายของ) แปลงออกมาเป็นตามนี้ครับ
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
data:
API_KEY: c29tZUFQSUtleQ==
PASSWORD: c3VwZXJTdHJvbmdQYXNzd29yZA==
PG_PASSWORD: aWRvbG1Ac3Rlcg==
หลังจากนั้น เราก็จะสามารถใช้คำสั่ง SOPS เพื่อเข้ารหัสข้อมูลของเราได้ตามคำสั่งนี้
$ sops --encrypt --azure-kv https://sops-key-test.vault.azure.net/keys/sops-test-key/25053b6b49074e83b00a42b22ffa897a secret.yaml
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:
...
โดยเราจะได้ไฟล์ที่เข้ารหัสแล้ว ถูกแสดงผลออกมาทาง CLI ซึ่งเราสามารถทำการเซฟลงไฟล์ได้ดังนี้
$ sops --encrypt --azure-kv https://sops-key-test.vault.azure.net/keys/sops-test-key/25053b6b49074e83b00a42b22ffa897a 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
ไฟล์ที่ได้ออกมาก็จะมีแค่ 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:
...
Decrypt the YAML
คำสั่งที่ใช้ในการถอดรหัสก็ง่ายมากๆ เพราะข้อมูลทุกอย่างที่ SOPS ต้องการจะใช้นั้น ถูกเก็บอยู่ในไฟล์ที่ถูกเข้ารหัสไว้อยู่แล้ว
# แสดงผลออกทาง CLI
$ sops --decrypt secret.enc.yaml
# ในกรณีที่ต้องการเซฟข้อมูลที่ถอดรหัสแล้วลงในไฟล์
$ sops --decrypt secret.enc.yaml > secret.yaml
That's It!!
เท่านี้แหละครับ สำหรับการใช้ SOPS และ Azure Key Vault เข้ารหัส Kubernetes Secret ของเรา การทำแบบนี้จะทำให้เราสามารถเก็บตัว Secret declaration ไว้ใน version control ที่เป็นสาธารณะได้ โดยไม่ต้องกลัวเรื่องการจัดการ key สักนิดเลยครับ