Skip to main content

Bỏ Linux capability để bảo vệ container của bạn

Đọc bài này Secure your containers one weird trick của một bác bên Redhat thấy hay nên mình tạm dịch ở đây. Ý của bác này là nên bỏ các Linux capability khỏi container, cần dùng capability nào thì thêm vào. Một kiểu như là quản lý không xuể thì nên cấm ngay từ đầu.

Bạn có biết có một tùy chọn có thể tắt Linux capabilities trong Docker? Sử dụng `docker run --cap-drop` bạn có thể khóa root trong container để nó có quyền truy cập giới hạn trong container.




Đáng buồn là hầu hết không ai từng thắt chặt security cho một container.

Trong bài này mình để nguyên từ "capability" trong "Linux capability" mà không dịch là "khả năng" để bạn biết đó là một khái niệm của Linux.

Ngày hôm sau là quá muộn

Có một xu hướng đáng buồn trong dân IT khi nghĩ về security khi đã quá trễ. Người ta chỉ chú ý đến security system ngay sau khi hệ thống đã bị bẻ gãy.

Gỡ bỏ capability có thể là một giải pháp đơn giản để cải thiện container security.

Thế nào là Linux Capabilities?

Theo như trang capabilities man page, capabilites là những đơn vị quyền (previlege) có thể được enable hoặc disable.

Đa số mọi người nghĩ rằng root là có tất cả quyền lực. Không hẳn như vậy, user root sở hữu tất cả các capability mới là có tất cả quyền lực. Capability được thêm vào kernel trong vòng 15 năm và cũng ngần ấy năm người ta cố gắng chia nhỏ quyền lực của root.

Ban đầu kernel được cấp phát bitmask 32 bit để định nghĩa các capability này. Một vài năm trước nó được mở rộng lên 64 bit. Hiện tại có khoảng 38 capability được định nghĩa.
Ta có thể xem danh sách 38 capability ở đây:

curl https://raw.githubusercontent.com/torvalds/linux/master/include/uapi/linux/capability.h | grep " CAP_" | awk '{print $2, $3}'

Các ví dụ của Capability như là khả năng gửi raw IP packets, hoặc là bind những ports < 1024. Khi chúng ta run các container chúng ta có thể bỏ một nhóm các capability trước khi chạy các container mà không làm cho phần lớn các ứng dụng bị fail.

Phần lớn các capability được yêu cầu để thao tác với kernel/system, và những capability này được sử dụng bởi container framework (docker), hiếm khi được sử dụng bởi các process chạy bên trong container. Tuy nhiên, một vài container yêu cầu một số capability, ví dụ một container process yêu cầu capability như setuid/setgid để bỏ đặc quyền (drop privileges). Trong thế giới của container, chúng ta cố gắng thỏa hiệp giữa security và khả năng hoàn thành công việc.

Một vài năm trước một số guys ở grsecutiry đã làm một vài phân tích trên capabilitty và phát hiện rât nhiều trong số capability có thể cho bạn đầy đủ quyền truy cập hệ thống.

May mắn thay chúng ta còn sử dụng những công cụ như SELinux, seccomp, và namespaces để bảo vệ host system khỏi các container.

Tóm lại, Từ góc nhìn của security, bỏ đi càng nhiều capability khỏi container càng tốt.

Chú ý: Khi một container framework bỏ capability trước khi start một container, các process bên trong container không thể mang các capability này trở lại, thậm chí nếu chúng thực thi một ứng dụng setuid. Để biết thêm thông tin, nhìn vào phần `Capability Bounding Set` trong trang capability man page.

Docker có những mặc định (default) nào?

Chúng ta hãy xem danh sách các capability sẵn sàng cho các tiến trình có đặc quyền (priviledged process) trong một docker container:

chown, dac_override, fowner, fsetid, kill, setgid, setuid, setpcap, net_bind_service, net_raw, sys_chroot, mknod, audit_write, setfcap

Trong OCI/runc thậm chí khắc khe hơn chỉ giữ lại duy nhất `audit_write`, `kill` và `net_bind_service` và user có thể sử dụng ocitools để bổ sung các capability. Bạn có thể thích cách tiếp cận là thêm các capability khi nào mình cần hơn là phải nhớ xóa các capability bạn không cần.

Đi sâu hơn vào Capability

chown

chown man page diễn tả chown là khả năng tạo thay đổi bất kỳ trên file UIDs và GIDs.

Điều này có nghĩa là root có thể thay đổi ownership hoặc group của bất kỳ đối tượng file hệ thống nào. Nếu bạn không chạy một shell trong container và không cài đặt package vào trong container, bạn nên bỏ capability này đi.

Điều này gây nên một tranh luận là nên chăng chẳng bao giờ cần capability này trên production. Nếu bạn cần chown, add capability này, làm xong việc, thì bỏ nó đi.

dac_override

Man page bảo rằng `dac_override` cho phép root bỏ qua các kiểm tra quyền đọc, ghi và thực thi file. DAC là từ viết tắt của "discretionary access control".

Điều này có nghĩa là một tiến trình chạy quyền root có thể đọc, ghi và thực thi bất kỳ file nào trên hệ thống, thậm chí nếu các trường permission và owner không cho phép làm điều đó. Hầu hết các ứng dụng không cần DAC_OVERRIDE, và nếu chúng cần chúng có thể làm sai một số thứ. Có lẽ ít hơn 10% ứng dụng thật sự cần tới nó. Tất nhiên administrator shell có thể yêu cầu DAC_OVERRIDE sửa các bad permission trong hệ thống file.

Steve Grubb, một chuyên gia về các chuẩn an ninh của Red Hat, nói rằng "không có gì cần thứ này. Nếu container của bạn cần nó, nó có thể làm điều gì đó khủng khiếp."

fowner

Theo như man page, `fowner` sở hữu khả năng vượt qua các kiểm tra quyền (permission check) trên các thao tác yêu cầu filesystem UID của process tương thích với UID của file. Ví dụ, `chmod` và `utime`, và không bao gồm các tác vụ của `cap_dac_override` và `cap_dac_read_search`. Đây là một số thông tin thêm từ man page:
* thiết lập các thuộc tính file mở rộng (xem chattr(1)) trên các file bất kỳ;
* thiết lập Access Control Lists (ACLs) trên các file bất kỳ;
* bỏ qua directory sticky bit khi xóa file; (sticky bit được mô tả bằng 1 chữ t. Chỉ duy nhất owner file (và root) mới có quyền rename hay xóa các file thư mục mà nó đã được set sticky bit).
* đặc tả O_NOATIME cho các file bất kỳ trong open(2) và fcntl(2).

Cũng tương tự với DAC_OVERRIDE, phần lớn không có ứng dụng nào cần nó hơn những cái khác, nhưng cũng có khả năng là các công cụ cài đặt phần mềm. Nhiều khả năng container của bạn chạy ổn mà không có capability này. Bạn có thể cần nó cho `docker build` nhưng nó có thể bị block khi bạn chạy container trên production.

fsetid

Man page nói rằng "đừng xóa các mode bit set-user-ID và set-group-ID khi một file bị thay đổi; thiết lập bit set-group-ID cho file mà GID của nó không tương thích với hệ thống file hoặc bất kỳ GID bổ sung nào của tiến trình gọi."
Nếu bạn không chạy một tiến trình cài đặt, bạn có thể không cần capability này. Tôi mặc định vô hiệu hóa (không dùng) cái này.

kill

Nếu một process có capability này nó có thể vượt qua hạn chế của hệ thống về việc "user ID của một process gửi một signal phải giống với user ID của tiến trình nhận signal."

Capability này căn bản có nghĩa là một process sở hữu bởi root có thể gửi kill signal cho một process không sở hữu bởi root. Nếu container của bạn chạy tất cả các process như root hoặc các process root chẳng bao giờ kill các process chạy không bởi root, bạn không cần capability này. Nếu bạn chạy systemd như PID 1 bên trong một container và bạn muốn dừng một container đang chạy với UID khác bạn có thể cần capability này.

setgid

Man page nói rằng `setgit` capability cho phép một process tạo ra các thao tác bất kỳ của các process GID và danh sách các GID bổ sung. Nó cũng có thể làm giả GID khi truyền socket credential qua UNIX domain socket hoặc viết một group ID mapping vào một user namespace. Xem user_namespaces(7) để biết thêm thông tin.

Tóm lại, một tiến trình với capability này có thể thay đổi GID của nó thành GID khác. Cơ bản là cho phép toàn quyền truy xuất nhóm lên tất cả các file trên hệ thống. Nó các process trong container của bạn không thay đổi UIDs/GIDs, chúng không cần capability này.

setuid

Nếu một process có capability `setuid` nó có thể tạo ra các thao tác bất kỳ của các process UID(setuid(2), setreuid(2), setresuid(2), setfsuid(2)); làm giả UID khi truyền socket credentials thông qua UNIX domain sockets,; viết một user ID mapping vào một user namespace (xem [user_namespaces(7)](http://man7.org/linux/man-pages/man7/user_namespaces.7.html)).

Một process với capability này có thể thay đổi UID của nó thành bất kỳ UID khác. Cơ bản, nó cho phép toàn quyền truy xuất lên tất cả các file trên hệ thống, Nếu các process trong container của bạn không thay đổi UIDs/GIDs, luôn luôn chạy với cùng UID, non root, chúng không cần capability này. Các ứng dụng cần `setuid` thường start as root để bind vào các port < 1024 và sau đó thay đổi UID của chúng và xóa capability này. Apache bind vào port 80 yêu cầu `net_bind_service`, thường start as root. Nó sau đó cần setuid/setgid để chuyển thành apache user và bỏ capability này.

Phần lớn các container có thể bỏ setuid/setgid capability một cách an toàn.

setpcap

Man page ghi : "Thêm bất kỳ capability nào từ bounding set của thread gọi cho những thread con của nó; bỏ capability từ bounding set (thông qua prctl(2) PR_CAPBSET_DROP); thay đổi các flag securebits."

Theo các thuật ngữ của layman, một process với capability này có thể thay đổi capability set hiện tại của nó trong bounding set của nó. Có nghĩa là một process có thể bỏ capability hoặc thêm capability nếu nó chưa có, những bị giới hạn bởi các capability bounding set.

net_bind_service

Cái này thì đơn giản. Nếu bạn có capability này, bạn có thể bind tới các priviledged ports (ví dụ những port nhỏ hơn 1024).

Nếu bạn muốn bind tới một port nhỏ hơn 1024 bạn cần capability này. Nếu bạn đang chạy một service lắng nghe một port trên 1024 bạn có thể bỏ capability này đi.

Rủi ro của capability này là một process giả mạo thông dịch một service như sshd, thu thập các user password. Chạy một container trong một network namespace khác làm giảm rủi ro của capability này. Nó có thể khó khăn cho process container trong việc lấy public network interface.

net_raw

Man page nói rằng "cho phép sử dụng các socket RAW và PACKET". Cho phép binding tới bất kỳ địa chỉ nào (transparent proxying)"

Truy xuất này cho phép một process spy trên các packet trên network của nó. Điều này thật tồi tệ. Phần lớn các process container không cần truy xuất này nên nó có thể được bỏ ra. Chú ý là nó chỉ ảnh hưởng lên các container chia sẻ chung network mà container process của bạn đang chạy, thường thì ngăn chặn các truy cập lên network thật.

RAW socket còn có thể cho các kẻ tấn công khả năng inject các thứ nguy hiểm lên network. Phụ thuộc vào những gì bạn đang làm với ping command, nó có thể yêu cầu truy cập này.

sys_chroot

Capability này cho phép sử dụng chroot(). Nói cách khác nó cho phép process của bạn chroot vào một rootfs khác. chroot thường không được sử dụng trong container của bạn, nó nên được bỏ đi.

mknod

Nếu bạn có capability này, bạn có thể tạo ra các file đặc biệt sử dụng mknod.

Capability này cho phép các process của bạn tạo ra các device nodes. Các container thường được cung cấp các device node chúng cần trong /dev, việc tạo ra các device node được điều khiển bởi device node cgroup, nhưng mình nghĩ rằng chúng ta nên mặc định bỏ nó đi. Phần lớn không có container nào làm cái này.

audit_write

Nếu bạn có cái này, bạn có thể viết một message lên kernel auditing log. Một số ít process cố gắng viết lên audit log (login programs, su, sudo) và các process bên trong của container thường không đáng tin. audit subsystem hiện tại không có namespace, nên bỏ capability này đi.

setfcap

Cuối cùng, `setfcap` capability cho phép bạn set file capability lên một file system. Có cần cho việc install hoặc build, nhưng trên production nó nên được bỏ ra.

Làm thế nào tôi có thể bỏ các capability này sử dụng Docker?

Có một tool có thể giúp bạn xem những capability nào một process có gọi là pscap

Trên ubuntu bạn cài đặt pscap:
sudo apt install libcap-ng-utils

Dưới đây là các capability của một container bình thường đang chạy:


Nếu bạn muốn bỏ `setfcap`, `audit_write` và `mknod`, bạn có thể sử dụng `--cap-drop=setfcap --cap-drop=audit_write --cap-drop=mknod`:


Nếu bạn muốn container chỉ cần 2 capability `setuid` và ``setgid``, bạn có thể bỏ tất cả các capability và chỉ thêm `setgid` và `setuid` vào lại.




Lời cuối

Bạn có thể đang chạy các container với nhiều hơn các đặc quyền mà chúng cần. Bỏ các capability này đi khi container chạy trên production có thể là một ý tưởng hay.

Tham khảo

1. https://www.redhat.com/en/blog/secure-your-containers-one-weird-trick
2. https://github.com/konstruktoid/Docker/blob/master/Security/CheatSheet.adoc
3. https://archlinuxvn.org/doc/systemd/lennart_changing_roots/

Comments

Popular posts from this blog

MongoDB chiếm nhiều RAM ?

A. Giới thiệu : MongoDB là một trong những NoSQL(Not oly SQL) database được biết đến nhiều hiện nay , đặc trưng với cách lưu trữ dạng binary Json (BSON)cũng như khả năng truy vấn uyển chuyển (các NoSQL khác thường giới hạn điều này ) . Shell script tương tác bằng javascript . Do đó nó hay đi chung với Node.js :) Tuy nhiên , có một vấn đề là mongoDb sử dụng nhiều dung lượng RAM (do cơ chế caching của nó). Giải pháp là phải tăng dung lượng RAM cho server đó , cũng như là monitoring cẩn thận để không xảy ra tình trạng này . Nhưng còn giải pháp nào khác nữa không ? Đầu tiên cần tìm hiểu các nguyên nhân MongoDB chiếm nhiều RAM như vậy , từ đó mới có cách giải quyết triệt để . Dưới đây là một số nguyên nhân mà tôi đã tìm hiểu được . B. Nguyên nhân : 1. Index : MongoDb có tốc độ đọc (read) nhanh , nếu ta biết cách tạo index hiệu quả . MongoDB dùng B-tree index (nhanh hơn table scans). Tuy nhiên index này lại nằm trên RAM . Khi bạn dùng câu lệnh "remove" để xóa các document tron

Luyện tập viết code

Dạo này đi phỏng vấn kiểu pair programming mới thấy sự cần thiết của việc rèn luyện code hằng ngày. Làm sao để tạo hứng thú luyện tập code đây? Dưới đây là danh sách các công việc khởi tạo ban đầu để hứng thú với code như sau:      1.  Trên github tạo repository chứa code để luyện tập. Ví dụ như MyHackerrank      2. Nếu có sẵn project rồi cần add vào github thì thực hiện các bước sau:             Tham khảo thêm project có sẵn vào github sử dụng command line        3. Tham gia một số cộng đồng coding như là Hackerrank và Leetcode. Hackerrank có phần  Hackerrank Interview Preparation Kit   cũng khá là hay ho, hồi xưa thời còn đi học làm gì có mấy trang kiểu này. Hoặc là  tập hợp các bài toán trên Leetcode . Ở leetcode cũng có một bộ sưu tập các câu hỏi phỏng vấn được đặt ra và trả lời bởi cộng đồng, cùng với các bài học về giải thuật và lập trình hướng tương tác rất hay.  Leetcode Explore         4. Ôn giải thuật và cấu trúc dữ liệu thì trang này khá đầy đủ  Geeksforgeeks&

ReplicaSet MongoDb

A. Giới thiệu : Replication được dùng để "hot backup" dữ liệu khi có sự cố xảy ra . MongoDb có 1 collection gọi là oplog (viết tắt của operation log) , master sẽ dùng collection này cho việc ghi và các slave sẽ truy vấn từ collection này . Mỗi lần ghi (insert,update, delete) sẽ tạo ra một document trong oplog collection (nếu sử dụng replication, không dùng thì mongoDb sẽ không dùng tới collection oplog này) . Để sử dụng replication , start mongod với option replSet. Truy vấn từ oplog xem ở đây (Mình xin dịch lại) rs.initiate() use local db.oplog.rs.find() { "ts" : { "t" : 1286821527000 , "i" : 1 } , "h" : NumberLong ( 0 ) , "op" : "n" , "ns" : "" , "o" : { "msg" : "initiating set" } } ts : thời gian thao tác thực hiện . h : một ID duy nhất cho thao tác này . Mỗi thao tác sẽ có một giá trị khác nhau trong trường này