Vault: เก็บ Secret และ Key เข้ารหัส

เก็บรหัสผ่าน API key และ Key เข้ารหัสไว้ที่เดียว โปรแกรมอ่านด้วย Service Account โดยไม่ต้องฝังค่าไว้ในโค้ด

เริ่มใช้งาน

  1. เปิด Vault ใน Console แล้วสร้าง Vault แยกตามแอปหรือสภาพแวดล้อม เช่น app-production
  2. เพิ่ม Secret หรือสร้าง Key
  3. เปิดแท็บ สิทธิ์การเข้าถึง แล้วให้สิทธิ์ Service Account ที่บริการของคุณใช้
  4. ในโค้ด ขอ Token แล้วเรียก API ตามตัวอย่างด้านล่าง

สิทธิ์ของ Service Account

สิทธิ์ทำอะไรได้
อ่าน Secretอ่านค่าของ Secret
อ่านและเขียน Secretอ่านค่าและเพิ่มเวอร์ชันใหม่
ใช้ Key เข้ารหัสและลงลายเซ็นเข้ารหัส ถอดรหัส ลงลายเซ็น ตรวจลายเซ็น
ใช้และหมุน Keyใช้ Key และสร้างเวอร์ชันใหม่
อ่าน Secret และใช้ Keyรวม 2 อย่างนี้
อ่านและเขียน Secret ใช้และหมุน Keyทุกอย่าง

ถ้ายังไม่ได้ให้สิทธิ์ Service Account จะใช้ Vault ไม่ได้ แม้อยู่ใน Project เดียวกัน ส่วนคนใน Project: Viewer และ Operator เห็นแค่ชื่อและประวัติ Editor ดูและแก้ค่าได้ Owner และ Admin ลบได้

ใช้ Secret เป็นตัวแปรของ Container หรือ Function

ใส่ค่าตัวแปรเป็น vault://app-production/db-password หรือกด ใช้ค่าจาก Vault ในหน้าตัวแปร โค้ดอ่านตัวแปรตามปกติโดยไม่ต้องแก้อะไร ระบบเปิดค่าจริงให้ตอนบริการเริ่มทำงาน ส่วน Console และ Revision เก็บแค่ที่อยู่นี้

ต้องให้สิทธิ์อ่าน Secret แก่ Service Account ของบริการก่อน ถ้าเพิ่มเวอร์ชันใหม่ ต้องรีสตาร์ตบริการจึงจะได้ค่าใหม่ ถ้าต้องการล็อกเวอร์ชัน ให้ต่อท้ายด้วยเลขเวอร์ชัน เช่น vault://app-production/db-password/2 ถ้าอ่านค่าไม่ได้ตอนเริ่มทำงาน เช่น ถูกถอนสิทธิ์ บริการจะไม่เริ่มและแจ้งสาเหตุไว้

อ่าน Secret

Shell
# Inside a Container or Function: a token from the machine it runs on
TOKEN=$(curl -s -H "Metadata: true" \
  "http://169.254.169.254/identity/oauth2/token" | jq -r .access_token)

curl -H "Authorization: Bearer $TOKEN" \
  https://api.thailandhosting.com/v1/vaults/app-production/secrets/db-password
# {"name":"db-password","version":2,"value":"...","expiresAt":null}

ไม่ระบุเวอร์ชันจะได้เวอร์ชันใหม่ล่าสุดที่ยังเปิดอยู่และยังไม่หมดอายุ ระบุเวอร์ชันด้วย ?version=2 ควรเก็บค่าไว้ในหน่วยความจำของโปรแกรมสักพัก แทนการเรียกทุกครั้ง

เข้ารหัสด้วย Key

Shell
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -d "{\"plaintext\":\"$(printf 'hello' | base64)\"}" \
  https://api.thailandhosting.com/v1/vaults/app-production/keys/customer-data/encrypt
# {"ciphertext":"thv1:2:...","version":2}

curl -X POST -H "Authorization: Bearer $TOKEN" \
  -d '{"ciphertext":"thv1:2:..."}' \
  https://api.thailandhosting.com/v1/vaults/app-production/keys/customer-data/decrypt
# {"plaintext":"aGVsbG8="}

ข้อมูลส่งเป็น base64 ได้ครั้งละไม่เกิน 64 KB ผลลัพธ์บอกเวอร์ชันของ Key อยู่ในตัว จึงถอดรหัสได้แม้หมุน Key แล้ว ถ้าส่ง aad ตอนเข้ารหัส ต้องส่งค่าเดิมตอนถอดรหัส

ข้อมูลใหญ่หรือมีจำนวนมาก ให้ขอ Data key ที่ /data-key ใช้ค่า plaintext เข้ารหัสเองในโปรแกรม แล้วเก็บ ciphertext ไว้คู่กับข้อมูล ตอนอ่านค่อยส่ง ciphertext ไปถอดรหัส

ชนิดของ Key

ชนิดใช้ทำอะไร
AES-256-GCMเข้ารหัส / ถอดรหัส
RSA 2048, RSA 4096เลือกอย่างใดอย่างหนึ่ง: เข้ารหัส / ถอดรหัส หรือลงลายเซ็น / ตรวจสอบ
ECDSA P-256, P-384ลงลายเซ็น / ตรวจสอบ

SDK

Shell
import { defaultCredential } from '@thailandhosting/identity';

const vault = defaultCredential().vault('app-production');

const secret = await vault.getSecret('db-password');
const ciphertext = await vault.encrypt('customer-data', 'hello');
const plaintext = await vault.decrypt('customer-data', ciphertext);
Shell
from thailandhosting_identity import default_credential

vault = default_credential().vault("app-production")

secret = vault.get_secret("db-password")
ciphertext = vault.encrypt("customer-data", b"hello")
plaintext = vault.decrypt("customer-data", ciphertext)
Shell
cred, err := identity.Default()
vault := cred.Vault("app-production")

secret, err := vault.GetSecret(ctx, "db-password")
ciphertext, err := vault.Encrypt(ctx, "customer-data", []byte("hello"), nil)
plaintext, err := vault.Decrypt(ctx, "customer-data", ciphertext, nil)

th CLI

Shell
th vault create app-production
th vault secret set app-production db-password   # reads the value from standard input
th vault secret get app-production db-password

th vault key create app-production customer-data --type aes256 --rotation-days 90
th vault key encrypt app-production customer-data 'hello'

คำสั่ง th vault ใช้สิทธิ์ของคนที่ th auth login API Key เรียกไม่ได้ ระบบอัตโนมัติและ CI ให้ใช้ Service Account แทน ส่วนการลบถาวรทำได้ใน Console เท่านั้น

ราคา

ตัว Vault ฟรี คิดตามจำนวน Secret และ Key ที่มีอยู่ (รวมทุกเวอร์ชัน) กับจำนวนครั้งที่โปรแกรมอ่าน Secret หรือใช้ Key ดูราคาที่หน้า ราคา

ไม่คิดเงิน: การอ่านค่าเป็นตัวแปรตอนบริการเริ่มทำงาน, การใช้ผ่าน Console และ th CLI, การดูรายการ เพิ่มเวอร์ชัน หรือหมุน Key, การเรียกที่ไม่สำเร็จ และรายการที่ลบแล้ว ถ้า Project มียอดค้างชำระ โปรแกรมจะเรียก Vault ไม่ได้จนกว่าจะเติมเงิน

ลบและกู้คืน

รายการที่ลบจะใช้งานไม่ได้ทันที แต่กู้คืนได้ภายในระยะเวลาที่ตั้งไว้ใน Vault (7-90 วัน) เมื่อครบกำหนดจะถูกลบถาวร ถ้าลบ Key ถาวร ข้อมูลที่เข้ารหัสด้วย Key นั้นจะเปิดไม่ได้อีกเลย

เปิด ป้องกันการลบถาวร เพื่อไม่ให้ใครลบถาวรก่อนครบกำหนด รวมถึงเจ้าของ Project เมื่อเปิดแล้วปิดไม่ได้

ประวัติการใช้งาน

แท็บ ประวัติการใช้งาน บอกว่าใครทำอะไร เมื่อไร จาก IP ไหน รวมถึงการเรียกที่ถูกปฏิเสธ การใช้งานจากโปรแกรมรวมเป็นรายการละชั่วโมงต่อผู้เรียก เก็บไว้ 1 ปี

ถ้าเจอปัญหา

ได้ 403 forbidden

Service Account ยังไม่มีสิทธิ์ที่ตรงกับคำสั่งนี้ ดูชื่อที่ถูกปฏิเสธได้ที่แท็บประวัติการใช้งาน แล้วให้สิทธิ์ที่แท็บสิทธิ์การเข้าถึง

เก็บ Key ไว้ใน HSM หรือไม่

ไม่ Key ถูกเข้ารหัสด้วยซอฟต์แวร์ และเก็บในฐานข้อมูลของระบบ ยังไม่มีแบบ HSM

ยังแก้ไม่ได้? ให้ทีมงานช่วย

เปิด ticket จากหน้าจัดการ บอกชื่อเว็บหรือบริการ สิ่งที่ทำ และข้อความที่เห็นบนจอ ทีมงานจะตอบกลับในเรื่องเดียวกัน ติดตามได้ในที่เดียว