Security trên Kubernetes [Phần 2]

1. Giới thiệu

Đã bao giờ bạn cảm thấy như mình đang cố gắng bảo vệ một pháo đài với hàng ngàn cánh cửa, trong khi đó trong tay bạn lại không có cái ổ khóa nào không? Chào mừng bạn đến với thế giới của Kubernetes (K8S), một thế giới với đầy những container (link: https://vietnix.vn/container-la-gi/) lộn xộn cùng những dòng log dài như mê cung. Việc bảo mật trong một thế giới như thế này luôn là một trò chơi mèo vờn chuột không hồi kết.

 

Và nếu bạn đang tự hỏi làm thế nào để có thể bảo vệ toàn bộ “pháo đài” K8S mà không cần phải kiểm tra qua từng cánh cửa, thì bạn đã đến đúng nơi rồi đấy. Hãy cùng tui khám phá cách sử dụng các “kết giới” Falco và kiến trúc Fluentbit + ElasticSearch để biến mọi cánh cửa trong pháo đài K8S của bạn trở thành một lớp bảo vệ kiên cố nhé!

 

Và cũng thật tình cờ, khi đặt bút viết những dòng này, tui mới nhận ra rằng một người bạn không cùng cha và cũng khác ông nội của tui đã từng viết một bài viết khá chi tiết về Falco. Link bài viết các bạn có thể xem ở đây: https://sec.vnpt.vn/2022/09/security-tren-kubernetes-phan-1/.

 

Như các bạn có thể thấy, “đứa con tinh thần” của thằng bạn tui mới chỉ dừng lại ở “phần 1”. Do đó, để tiếp nối sự nghiệp còn đang dang dở (và cũng vì mục đích hưởng ké tí Fame) mà tui đã đặt tên cho bài viết này là “Security trên Kubernetes phần 2”.

 

2. Tóm tắt lại bài cũ: Falco

Mặc dù bài viết phần 1 đã viết khá chi tiết về Falco rồi, nhưng dù sao thì bài viết đó tới nay cũng đã được gần 2 năm tuổi. Anh em ngày xưa đi học còn chữ thầy trả thầy, học bài ngay từ hôm trước đến hôm sau đã quên sạch, huống gì những thứ này đã được viết từ cách đây 2 năm ^^.

 

Được rồi, hãy tóm tắt lại một chút về “kết giới” Falco nào.

 

Về khái niệm, Falco là một công cụ mã nguồn mở dùng để phát hiện rủi ro và các mối đe doạ tốt nhất trên các nền tảng K8S, containers, cloud & on premise. Falco được ví như một camera an ninh, liên tục phát hiện các hành vi đáng ngờ, các thay đổi về cấu hình, xâm nhập và đánh cắp dữ liệu. Hiện tại thì Falco đã và đang được “bảo kê” bởi CNCF (Cloud Native Computing Foundation).

 

Hình 1: Kiến trúc tổng quan của Falco

 

Như bài viết phần 1 đã chia sẻ, kiến trúc của Falco sẽ gồm 3 thành phần chính:

  • Module eBPF: tương tác với Kernel hệ thống ghi lại các thao tác liên quan tới: chạy lệnh gì, thông tin ra sao, gọi tới các hàm nào…
  • Filter Expression: cung cấp bộ module rule engine thực hiện chức năng lọc ra các thông tin sự kiện và xuất ra logs.
  • Alerting: Falco có 5 đầu ra cho các event của nó: stdout, file, gRPC, shell và http. Thêm vào đó có thể sử dụng công cụ Falcosidekick để mở rộng output.

Tuy nhiên khái niệm chỉ là một phần nhỏ. Làm gì có ai có thể thả diều trong khi chỉ biết mỗi khái niệm về con diều, đúng không nào! Do đó hãy cùng tui tìm hiểu cách để có thể tận dụng năng lực của “kết giới” Falco nhé.

Đầu tiên, chúng ta cần xem log của Falco có dạng như thế nào. Tèn ten, đây là 1 dòng log mà tui “thó” được từ một trong các máy chủ mà tui đang đảm nhận. Tất nhiên là phải che đi những thông tin nhạy cảm rồi.

Hình 2: Định dạng log của Falco

 

Ồ, mới nhìn qua thì các bạn có biết đây là log gì không? Hãy chú ý vào những chỗ mà tui đã bôi đỏ. Chắc một vài anh em cũng đoán ra được rồi, đây là log detect của các rule trong Falco. Log này sẽ có các thông tin cơ bản như 

  • proc.cmdline: lệnh mà người dùng đã thực hiện qua command line
  • proc.name: tên của lệnh được gọi
  • user.name: người dùng đã thực hiện lệnh này
  • rule: rule đã detect

Vậy anh em đã hình dung ra được cách mà chúng ta sẽ tận dụng “kết giới” này chưa? Đúng rồi đó, chúng ta sẽ viết các rule để detect ra các lệnh gọi của người dùng mà có khả năng cao là độc hại. 

Vậy các lệnh như thế nào là độc hại? Tui sẽ lấy ví dụ về một vài lệnh rất cơ bản:

# cat /etc/passwd

# cat ../../../../../destination_file.txt

# chmod 777 exc_file.sh

# systemctl disable abc.service

# mount /dev/sda4 /home/tmp/

Nếu những lệnh trên được thực hiện bởi một quản trị viên hệ thống thì có vẻ như hành động này hoàn toàn bình thường. Nhưng nếu nó được thực hiện bởi một người dùng với quyền rất thấp thì sao? Vâng, đúng rồi đó. Kể cả với các anh em mới nhập môn An toàn thông tin cũng có thể biết, việc một người dùng có quyền thấp mà lại muốn đọc thông tin của file passwd, hoặc lại muốn thiết lập full quyền cho một file thực thi, thì điều đó không bình thường một chút nào cả. Hoặc một vài lệnh đặc biệt nguy hiểm như “rm -rf” thì tui tin chắc là kể cả quản trị viên hệ thống cũng không bao giờ chạy lệnh này.

 

Vậy làm thế nào để viết một rule ngăn chặn các lệnh độc hại này? Cũng không quá phức tạp như trong truyện tranh khi mà nhân vật chính phải đọc cả một quyển sách ma pháp dày cộp để có thể triển khai kết giới. Hãy cùng tui xem qua một ví dụ về rule của Falco:

Hình 3: Cấu trúc rule của Falco

 

Đây là một rule mà tui cóp nhặt được trên mạng. Nhìn qua các thành phần của rule, chúng ta thấy rằng:

  • rule: Rất đơn giản và dễ đoán, trường này chính là tên của Rule. Để tránh nhầm lẫn thì anh em nên đặt các tên khác nhau cho các rule khác nhau nhé.
  • desc: Viết tắt của Description. Vâng, nó là mô tả của rule.
  • condition: Điều kiện của rule. Nếu bất cứ lệnh hệ thống nào phù hợp với các điều kiện này thì rule sẽ phát ra cảnh báo và ghi vào log.
  • enabled: Xác định xem rule có được bật hay không.
  • output: Đầu ra của cảnh báo.
  • priority: Mức độ ưu tiên của rule.
  • tags: Là các tag liên quan của rule. Phần này chủ yếu để người quản trị hệ thống có thể truy vấn hoặc gộp nhóm các rule dễ dàng hơn.

 

Tiếp theo, hãy nhìn sâu hơn vào trường “condition”:

  • Đầu tiên, ở mỗi điều kiện đều có một cụm từ là “spawned_process”. Nếu anh em biết sâu hơn về system thì sẽ biết rằng “spawn” một tiến trình có nghĩa là tạo ra một tiến trình độc lập hoàn toàn mới. Nó khác với việc “fork” một tiến trình có nghĩa là tạo ra một tiến trình con từ một tiến trình cha, sau đó 2 tiến trình này sẽ sao chép không gian bộ nhớ và chạy độc lập.
  • proc.name: Giống như phía trên tui đã giải nghĩa, đây là tên của lệnh thực thi
  • proc.args: Các tham số của lệnh thực thi.

=> Từ đó chúng ta có thể đoán được nội dung của rule này. Đó là nó sẽ phát ra cảnh báo nếu người dùng thực hiện các lệnh như “rm -rf”, “mkfs.ext4 /dev/sda1 -E nodiscard”, “systemctl disable apport.service”, …

 

Được rồi, vậy là anh em đã biết cách để viết 1 rule trong Falco rồi đúng không. Việc ôn lại bài cũ chỉ cần tới đây thôi. Nếu anh em muốn biết chi tiết hơn về cách triển khai và cấu hình Falco thì hãy lục lại bài viết phần 1 nhé.

 

3. Kiến trúc Fluentbit + ElasticSearch

Nếu coi Falco như một lá chắn ma pháp, thì kiến trúc Fluentbit + ElasticSearch giống như một tấm kiên vật lý vậy. Chúng ta không thể bảo vệ pháo đài nếu như chỉ tập trung vào việc ngăn chặn ma pháp. Ngoài những gã phù thủy độc ác, đối phương cũng sẽ có cả những chiến binh man rợ sẵn sàng lao vào càn quét bất cứ ai dám cản đường. 

 

Trước khi tìm hiểu về “tấm khiên” Fluentbit, hãy để tui nói rõ hơn về hình ảnh ví von thú vị mà tui vừa đề cập ở trên. Vâng, tất nhiên rồi, “pháo đài” ở đây chính là hệ thống Kubernetes của chúng ta. “Ngăn chặn ma pháp” tức là ngăn chặn các câu lệnh độc hại nhắm tới hệ thống. Còn hành động “tấn công càn quét” có thể được ví như các cuộc tấn công trên mạng, các cuộc tấn công DoS, DDoS, BruteForce, …

 

Vậy Fluentbit là gì? Đây là một công cụ mã nguồn mở dùng để thu thập và xử lý dữ liệu từ nhiều nguồn khác nhau. Fluentbit cố gắng cấu trúc dữ liệu dưới dạng JSON càng nhiều càng tốt. Điều này cho phép công cụ có thể hợp nhất tất cả các khía cạnh khác nhau của việc xử lý dữ liệu: thu thập, lọc, buffer và xuất log tới nhiều nguồn và đích.

 

Nói nôm na cho dễ hiểu, trong khi Falco hỗ trợ chúng ta ghi lại log thực thi của người dùng, thì Fluentbit sẽ ghi lại loc access. Log này sẽ ghi lại hầu hết các API call khi người dùng thực hiện các truy vấn qua mạng.

 

Thực ra ngoài Fluentbit thì còn một công cụ ưu việt hơn đó là Fluentd. Nhưng tất nhiên, cái gì ưu việt hơn thì nó sẽ nặng hơn và cấu hình phức tạp hơn (chi tiết anh em có thể tham khảo bài viết này trên chính trang chủ của Fluentbit: https://docs.fluentbit.io/manual/about/fluentd-and-fluent-bit). Do đó tui sẽ không sử dụng Fluentd trong bài viết này.

 

Được rồi, hãy cùng xem qua kiến trúc tổng quan của Fluentbit + ElasticSearch:

Hình 4: Mô hình tổng quan về sự kết hợp của Fluentbit, ElasticSearch và Kibana

 

Tui sẽ không nói sâu về ElasticSearch và Kibana trong phần này, vì nó đã rất phổ biến và gần như những anh em nào làm nhiều về xử lý dữ liệu lớn thì cũng đều đã sử dụng qua thằng này. Trong mô hình này:

  • Fluentbit đóng vai trò như các Agent, lắng nghe các lệnh API call từ các node, sau đó đẩy dữ liệu về phía cơ sở dữ liệu
  • ElasticSearch đóng vai trò như cơ sở dữ liệu. Ngoài ElasticSearch ra anh em hoàn toàn có thể sử dụng các công cụ khác như MongoDB, hoặc InfluxDB. Tất nhiên hãy đảm bảo rằng đó là kiến trúc NoSQL nhé. Fluentbit hầu như chưa hỗ trợ đẩy dữ liệu có dạng SQL.
  • Kibana đóng vai trò là thành phần phân tích log. Chúng ta cũng sẽ viết luật dựa trên kibana để phát hiện ra các hành vi bất thường khi truy cập mạng của người dùng.

 

Vậy cách triển khai Fluentbit trên hệ thống Kubernetes như thế nào? Tui sẽ tóm tắt lại các bước chính như sau:

Đầu tiên, tất nhiên rồi, anh em cần có hạ tầng K8S đã được cài sẵn. Anh em có thể cài nhanh phiên bản mini của K8S trên localhost bằng cách sử dụng minikube

Tất nhiên, cũng phải đảm bảo rằng kubectl đã được cài đặt nhé. Xem thêm trong link: https://kubernetes.io/docs/tasks/tools/

Tiếp theo, hãy định nghĩa RBAC (Role Based Access Control) cho Fluentbit. 

$ kubectl create namespace fluentbit-test

 

Để thu thập nhật ký từ các ứng dụng Kubernetes và các thành phần cụm, chúng ta cần cung cấp danh tính cho Fluent Bit và cấp cho nó một số quyền. Hãy tạo 1 file yaml như sau:

apiVersion: v1
kind: ServiceAccount
metadata:
name: fluent-bit
namespace: fluentbit-test

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: fluent-bit-read
rules:
– apiGroups: [“”]
resources:
– namespaces
– pods
verbs: [“get”, “list”, “watch”]

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: fluent-bit-read
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: fluent-bit-read
subjects:
– kind: ServiceAccount
name: fluent-bit
namespace: fluentbit-test

Hãy đặt tên nó là rbac.yaml. Đây là file manifest dùng để cấu hình RBAC. Tiếp theo hãy chạy:

$ kubectl create -f rbac.yml

Sau đó, anh em cần định nghĩa Config Map của Fluentbit. Có thể hiểu nôm na như nó là một bảng định nghĩa để cho Fluentbit biết nó sẽ ghi những log gì, ghi dưới dạng nào, định nghĩa các trường ra làm sao, lưu vào đâu, … Anh em có thể xem file mặc định tại đây: 

https://github.com/fluent/fluent-bit-kubernetes-logging/blob/master/output/elasticsearch/fluent-bit-configmap.yaml

Hãy tải file trên về và chỉnh sửa nó. Ngoài việc định nghĩa các trường dữ liệu ra, hãy nhớ thay đổi các giá trị “FLUENT_ELASTICSEARCH_HOST” và “FLUENT_ELASTICSEARCH_PORT” tương ứng với Host và Port của ElasticSearch mà bạn cần lưu dữ liệu.

Sau khi chỉnh sửa theo đúng ý của anh em, hãy chạy lệnh sau:

$ kubectl create -f <đường dẫn tới file yaml phía trên>

Khi đó, Config Map mới sẽ được tạo. Tiếp theo, chúng ta cần tạo Daemon Set cho Fluentbit. Hãy xem file mặc định tại đây:

https://github.com/fluent/fluent-bit-kubernetes-logging/blob/master/output/elasticsearch/fluent-bit-ds.yaml

 

Hoặc nếu anh em dùng Minikube, hãy sử dụng file sau:

https://github.com/fluent/fluent-bit-kubernetes-logging/blob/master/output/elasticsearch/fluent-bit-ds-minikube.yaml

 

Cũng làm tương tự như trên: Tải về và chỉnh sửa. Nhớ khai báo các biến Host và Port của ElasticSearch nhé anh em.

 

Sau đó, hãy chạy lệnh sau để tạo DaemonSet:

$ kubectl create -f <đường dẫn tới file yaml phía trên>

Sau khi chạy xong, ae có thể kiểm tra Kibana của mình. Ae sẽ thấy log của Fluentbit có dạng như sau:

Hình 5:  Cấu trúc log của Fluentbit

 

Đây là log mà tui cop nhặt được ở trên mạng. Tất nhiên log của công ty tui thì rất nhạy cảm nên tôi sẽ không đăng ở đâu. Tuy nhiên thì định dạng của chúng cũng không khác nhau là mấy. Qua hình trên ae có thể thấy Fluentbit đã ghi lại log access của nginx.

 

Dựa trên các log này, chúng ta có thể viết rule trên Kibana để phát ra các cảnh báo nếu có các hành vi bất thường. Ví dụ chúng ta có thể viết rule cảnh báo khi số lượng các gói tin gửi tới quá nhiều trong vòng 1 phút. Ngoài ra nếu trang web sử dụng giao thức HTTP (không có SSL/TLS) thì thậm chí Fluentd có thể bắt được cả payload của gói tin. Khi đó chúng ta hoàn toàn có thể viết rule Kibana dựa trên OWASP. 

 

Về cách viết rule của Kibana thì tui sẽ không đề cập ở đây, nhưng tui nghĩ nó cũng quá phổ thông với anh em rồi. Cho những ae chưa biết thì hãy tham khảo link sau: 

https://www.elastic.co/guide/en/kibana/current/security-best-practices.html

 

Ngoài ra hiện nay ElasticSearch cũng đang bắt đầu phát triển các tính năng liên quan tới Machine Learning để phân tích dữ liệu lớn. Tui thì chưa thử, nhưng tui tin chắc là trong tương lai ae hoàn toàn có thể sử dụng Machine Learning để phát hiện các hành vi bất thường dựa trên log của Fluentbit.

 

4. Kết luận

Trong bài viết này thì tui đã giới thiệu sơ qua về các “kết giới” Falco, Fluentbit cũng như cách áp dụng chúng vào việc cải thiện khả năng bảo mật của “pháo đài” K8S. Hy vọng với những kiến thức ít ỏi trên thì ae sẽ có cái nhìn tổng quan hơn về security trong K8S, giúp anh em có thể “thủ thành” vững chãi trước các cuộc tấn công không hồi kết. 

 

By,

Blue_Team

1.297 lượt xem