SSO Authentication ด้วย Sidecar Containers
ในบทความนี้เราจะมาประยุกต์ใช้ Sidecar pattern เพื่อนำ Logic ของ Authentication ออกจาก Application Layer พร้อมกับ Reuse ใช้กับ Service อื่นๆ โดยที่ไม่ต้องสนใจเรื่องของ Programming Language
Let’s go
ตัวอย่างของบทความนี้ คือระบบที่ทำ Single Sign-On (SSO) ผ่าน Keycloak และมี Backend for frontend (BFF) มาคั่นกลางระหว่างเรา (ไม่ใช่) เป็นคนกลางระหว่าง Client กับ Legacy Service เช่น ถ้า Client ต้องการข้อมูลของ User จะเรียกผ่าน API ของ BFF และ BFF จะเรียกไปที่ User Service เพื่อ Custom Response ให้ได้หน้าตาตามที่ Client ต้องการ ส่วน Pros and Cons ของ BFF pattern ลองไปหาอ่านเพิ่มเองนะ กลับมาที่ตัวอย่าง ระบบปัจจุบันจัดการ Authentication ที่ Application Layer โดย Flow คร่าวๆ จะเป็นประมาณนี้
- Request access token: User เข้าสู่ระบบผ่านหน้า SSO ที่ Keycloak, เมื่อเข้าสู่ระบบสำเร็จ access_token จะถูกจัดเก็บไว้ที่ cookie
- Invoke service api: ทุกครั้งที่ Client ต้อง Request มาที่ BFF จะต้องแนบ access_token เพื่อตรวจสอบสิทธิ์ในการใช้งาน
- Route to BFF: Ingress controller ทำหน้าที่ Route ไปที่ BFF
- Retrieve access token: ใน Layer ของ Application เราเขียน Middleware Function ขึ้นมา 1 ตัว มีหน้าที่นำ access_token จาก Client ไปเช็คที่ Keycloak ถ้า Active จะอนุญาตให้เข้าถึง Service แต่ถ้า Inactive ระบบจะคืนสถานะ Unauthorized (ฉันไม่ให้คุณผ่านเข้ามาในใจฉันหรอกนะ)
Sidecar Authentication Approach
Sidecar pattern คือ Single Pod ที่มี 2 Containers โดยที่ Container แรก คือ Main application ที่เก็บ business logic ของธุรกิจ เช่น Service สำหรับ Receiving (รับสินค้า), Service Storage (เก็บรักษาสินค้า) เป็นต้น.
ส่วน Container อีกตัวคือ พระเอกของเรา พ่อหนุ่มรูปหล่อเมืองหางโจวมีนามว่า Sidecar ใช้ปรับปรุงฟังก์ชันการทำงานของ Main application เช่น Log Aggregator, Resource limits, Retry request, Encryption Decryption data หรือในตัวอย่างนี้เราสามารถใช้ Sidecar มาคั่น Traffic เพื่อทำ Authentication
จากรูปก่อนหน้า ลำดับที่ 1 และ 2 ยังทำงานเหมือนเดิม ส่วนลำดับที่ 3 จากเดิมที่ Ingress Controller จะ Route ไปหา BFF ขั้นตอนนี้จะเปลี่ยนไป Route แถวๆ เสนานิคม นั่นมันร้าน เรา!! จะเปลี่ยนไป Rote หา Auth Proxy (Sidecar) และตัว Auth Proxy มีหน้าที่เหมือน Middleware คือเอา access_token จาก Client ไปเช็คที่ Keycloak ถ้า Active จะ Proxy ต่อไปให้ BFF ถ้า Inactive จะคืนสถานะ Unauthorized ไปให้ Client
Pros
- Middleware หรือ Libraries มีข้อจำกัดเรื่อง Programming language การที่เราใช้ Sidecar pattern นั่นเพิ่มความยืดหยุ่นให้เราสามารถ Reuse ส่วนของ Identity Authentication และ Token management ไปใช้กับ Service อื่นๆ โดยที่ต้องไม่สนใจเรื่องภาษาที่ใช้ในการพัฒนา
- ลดความซ้ำซ้อนของโค้ด เนื่องจากคุณไม่จำเป็นต้องเขียน Logic ของ Authentication แต่ Service
- Test ง่ายขึ้น เราสามารถให้ Proxy แนบข้อมูลของ User ให้กับ Application ผ่าน Headers สิ่งนี้ช่วยให้ Testing โดยเราสามารถ Create a mock proxy for testing
Cons
- เพิ่ม Resources ของ Pod เพื่อ Run 2 Containers
- เพิ่มความซับซ้อนอีกขั้นให้กับ Architecture เช่น ขั้นตอนการ Debug, การเช็ค Connection, การ Deploy
- หาก Sidecar มีปัญหา สามารถทำระบบทั้งหมดล้มได้ เนื่องจาก Sidecar มีเป้าหมายเพื่อแยกบริการหลักออกจากฟังก์ชันการทำงานเพื่อให้สามารถนำไป Reuse ใช้กับส่วนอื่นๆ
Sample application architecture
The full code for the authentication proxy can be สนุก on my Github
Pre-requisites:
- Kubernetes (เราใช้ k3d https://k3d.io/stable)
- Docker
kubectl
command
ผมสร้าง Sample Application ที่ใช้ Sidecar pattern ใครสนใจสามารถติดต่อได้ที่ … ล้อเล่น!! ลอง Clone แล้ว Run เล่นได้นะครับ ถ้า Run ขึ้นบอกผมด้วยนะ
- bff: รวม web-bff (rust), mobile-bff(go) ที่ทำหน้า Custom Response ตาม Client
- services: รวม Service ต่างๆ เขียนด้วย Node ใช้ Turbo repo มาช่วยทำ Monorepo
- auth-proxy: Source code ของ Sidecar ที่ทำหน้านำ
access_token
ไปเช็คที่ Keyclock - keycloak: ไฟล์ Config สำหรับ Setup Keycloak ทำเป็น External Service ที่ run ด้วย Docker