Các phương pháo bảo mật container (thực tế)
*Ký bút: devopsedu.vn kế thừa tư tưởng của elroydevops.tech luôn cung cấp kiến thức DevOps thực tế và chi tiết. Mình không cho phép bản thân làm những kiến thức mà mình không tin rằng nó tốt cho mọi người được. Nếu những bài viết dài khiến bạn không thể kiên nhẫn, thì kiến thức này dành cho những ai thật sự cần kiến thức thực tế.
Mạnh, Founder
Container là một trong những công nghệ, cách triển khai được sử dụng thịnh hành nhất trong các dự án có tính mở rộng hiện nay trên toàn thế giới. Vì tính dễ dàng sử dụng và nhanh chóng cũng như đảm bảo đáp ứng tốt yêu cầu mở rộng các dự án lớn và tối ưu chi phí khi sử dụng Kubernetes thì biết cách sử dụng container là điều bắt buộc không chỉ riêng DevOps Engineer mà cả Developer, Operator,… đều cần phải biết.
Những lý thuyết hay ngay cả việc sử dụng, triển khai bằng container trên internet đã rất nhiều. Tuy nhiên, làm sao để triển khai sử dụng container đảm bảo bảo mật và tối ưu trong thực tế thì không phải điều bạn có thể tìm kiếm dễ dàng. Cũng chính vì vậy kiến thức sử dụng các công nghệ nói riêng và DevOps nói chung rất khan hiếm kiến thức thực tế.
Bảo mật Container là gì ?
Bảo mật container đề cập đến việc bảo mật các ỨNG DỤNG được chứa trong container và cơ sở hạ tầng hỗ trợ chúng. Do tính chất không được lưu trữ lâu dài của container cũng như quá trình triển khai và mở rộng quy mô linh hoạt vì thế các biện pháp bảo mật cho container phải được tự động hóa và đưa vào mọi giai đoạn của vòng đời phát triển phần mềm.
Tại sao bảo mật container lại quan trọng ?
Vì việc container hóa đóng vai trò quan trọng trong quá trình phát triển phần mềm ngày nay nên bảo mật container là điều vô cùng cần thiết đối với các tổ chức, doanh nghiệp.
Các container chia sẻ cùng một kernel với máy chủ, vì vậy nếu một container bị xâm nhập thì có nguy cơ kẻ tấn công có thể khai thác lỗ hổng tiềm ẩn để ảnh hưởng để ảnh hưởng đến hệ thống máy chủ và các container khác bên trong máy chủ đó.
Vì các container được tạo từ image và những image này có thể chứa lỗ hổng nên kẻ tấn công khai thác chúng có thể giành được quyền truy cập vào các thành phần khác của hệ thống.
Đảm bảo an toàn là điều tối quan trọng để bảo vệ dữ liệu nhạy cảm, duy trì tính toàn vẹn trong hoạt động và bảo vệ khỏi các hành vi tiềm ẩn có thể gây ra hậu quả nghiêm trọng.
Các thành phần trong kiến trúc bảo mật Container
Kiến trúc bảo mật container được thiết kế để bảo vệ container trong suốt vòng đời. Quá trình này bao gồm một số thành phần chính, mỗi thành phần tập trung vào một khía cạnh khác nhau của việc bảo mật Container. Các thành phần chính bao gồm:
- Bảo mật image của Container (Container images security).
- Bảo mật nơi chứa các image của Container (Registry security).
- Bảo mật Container (Container engine security).
- Bảo mật các điều phối hoạt động Container (Orchestrators security).
Các phương pháp thực tế để bảo vệ container
1. Sử dụng base image tin cậy
Tránh sử dụng các image từ các nguồn không xác định vì chúng có thể chứa mã độc. Khi có thể, hãy sử dụng image được cung cấp chính thức của các nhà cung cấp hoặc tổ chức phần mềm. Những image này thường được nhà cung cấp hoặc tổ chức kiểm tra, xác minh và bảo trì, đảm bảo chất lượng và tính bảo mật.
Sử dụng signed image là các image được ký số bởi nhà phát hành image hoặc với một bên thứ ba đánh tin cậy, quá trình ký số này giúp đảm bảo tính toàn vẹn và xác thực của image Docker để đảm bảo image không bị giả mạo hoặc sửa đổi kể từ khi được tác giả ký.
Ví dụ đây là các image chính thức, xác thực, được tài trợ trên hub.docker.com (bạn có thể xem các ký hiệu bên cạnh image):
2. Sử dụng base image tối ưu
Một image được triển khai dựa trên kernel cụ thể ví dụ như alpine, centos, ubuntu, rockylinux, amazonlinux,… nên khi lựa chọn một base image nếu có thể hãy sử dụng alpine vì nó cực kỳ nhẹ và ít thành phần giúp hạn chế được rất nhiều những lỗ hổng và tối ưu được image.
Dưới đây là các image với các base khác nhau bạn có thể thấy size alpine nhẹ nhất vì nó chỉ chứa tối thiểu những thành phần:
Cũng tương ứng như vậy ví dụ bạn cần triển khai dự án Frontend bằng NodeJS thì nên sử dụng các base image Node với hậu tố là alpine, slim sẽ nhẹ, tối ưu nhẹ hơn rất nhiều so với các image đầy đủ:
3. Chỉ định chính xác phiên bản image
Sử dụng các phiên bản chính xác của image sẽ giúp bạn dễ dàng kiểm soát sự thay đổi nâng cấp của dự án. Tuy nhiên, chỉ nên sử dụng version ở cấp thứ nhất.
Ví dụ, khi bạn đang sử dụng image alpine thì bạn nên sử dụng alpine:3 và không nên sử dụng cụ thể alpine:3.x.y vì sau mỗi lần build mặc định hệ thống sẽ lấy image alpine:3 mới nhất từ hệ thống của hub.docker.com mà image đó có thể được vá những lỗ hổng bảo mật tốt hơn so với phiên bản được chỉ định chính xác đến các cấp nhỏ hơn.
4. Chỉ cài đặt những thành phần cần thiết
Sử dụng image tối giản chỉ cài đặt những phần mềm cần thiết. Điều này làm giảm khả năng tấn công vào ứng dụng. Việc sử dụng image nhẹ cũng sẽ tăng hiệu suất và thời gian khởi động container.
Ngoài ra, chúng ta sẽ tối giản Dockerfile để Image sau khi được build ra sẽ được giữ ở mức tối thiểu nhất có thể. Tránh bao gồm các gói không cần thiết hoặc để lộ cả Cổng được Expose để giảm khả năng bị tấn công. Khi bạn càng đưa nhiều thành phần vào trong một container, hệ thống của bạn sẽ càng dễ bị lộ và càng khó bảo trì, đặc biệt là đối với các thành phần không nằm trong tầm kiểm soát của bạn.
Ví dụ bạn muốn thêm chức năng kiểm tra trạng thái mạng trên mỗi thiết bị (netstat) thì chỉ cài đúng thêm công cụ đó. Hoặc bạn muốn kết nối tới postgresql thì chỉ cần cài đặt đúng postgresql client (trong quá trình làm việc mình đã thấy có bạn “tiện tay” cài đặt cả các database server lên container chạy dự án).
5. Xây dựng image nhiều tầng (multistage builds)
Việc sử dụng nhiều giai đoạn trong khi build image sẽ giúp cho image cuối cùng được nhỏ nhất, tối ưu nhất, bảo mật hơn so với việc sử dụng only stage (dù bạn có sử dụng bao nhiêu stage nhưng size image chỉ dựa vào base image cuối cùng và thành phần trong nó).
Ví dụ, bạn đang cần triển khai dự án Java và đang muốn build một docker image có thể chạy dự án Java đó. Vậy để chạy được dự án Java chúng ta sẽ phải có file .jar mà để có được file .jar đó thì sẽ cần phải build dự án bằng Maven, lúc này chúng ta sẽ sử dụng 2 stage.
Stage 1 với base image là Maven rồi tiến hành build dự án, build dự án thành công được file .jar thì sang stage 2 chỉ cần sử dụng base image có Java và copy file .jar từ stage 1 đã build được và tiến hành chạy. Ví dụ chi tiết dưới đây.
##### Dockerfile ##### ## build stage ## FROM maven:3.8.3-openjdk-17 as build WORKDIR ./src COPY . . RUN mvn install -DskipTests=true ## run stage ## FROM eclipse-temurin:17.0.8.1_1-jre-ubi9-minimal ## thay tương ứng các image đề xuất ở trên nếu không chạy RUN unlink /etc/localtime;ln -s /usr/share/zoneinfo/Asia/Ho_Chi_Minh /etc/localtime COPY --from=build src/target/filename.jar /run/filename.jar EXPOSE 8080 ENV JAVA_OPTIONS="-Xmx2048m -Xms256m" ENTRYPOINT java -jar $JAVA_OPTIONS /run/filename.jar
6. Cập nhật image thường xuyên
Vì các lỗ hổng bảo mật được phát hiện liên tục, nên cách tốt nhất về bảo mật chúng là sử dụng các bản vá bảo mật mới nhất. Vì thế ta cần thường xuyên cập nhật base image và xây dựng lại ứng dụng (docker image) của bạn.
Tuy rằng chúng ta cần cập nhật image thường xuyên để sử dụng các bản vá bảo mật mới nhất của image nhưng lại không nên sử dụng image latest version điều này có thể gây ra những thay đổi không mong muốn (tiếp nối ý phương pháp thứ 3).
- Sử dụng các phiên bản ổn định hoặc hỗ trợ dài hạn: Những phiên bản này cung cấp các bản sửa lỗi bảo mật sớm và thường xuyên.
- Lên kế hoạch sẵn sàng từ bỏ các phiên bản cũ và di chuyển trước khi phiên bản base image của bạn end of life và ngừng các bản cập nhật.
Ngoài ra, hãy xây dựng lại image của riêng bạn theo định kỳ và theo một chiến lược tương tự để nhận được các package mới nhất từ hệ điều hành cơ bản, Node, Golang, Python… Hầu hết các trình quản lý gói hoặc các dependency như npm hoặc go mod sẽ cung cấp các cách để chỉ định phạm vi phiên bản nhằm duy trì các bản cập nhật bảo mật mới nhất.
Các phần này trong series DevOps for Freshers (kiến thức thực tế) mình cũng đã có giảng giải hướng dẫn cụ thể ở phần container rồi.
7. Chỉ expose port cần sử dụng
Mỗi port được mở trong container của bạn đều là một CÁNH CỬA mở vào hệ thống của bạn. Chỉ nên expose các port mà ứng dụng của bạn cần (dự án bạn sử dụng port 8080 thì chỉ expose port 8080) và tránh expose các port như SSH (22).
Ví dụ trong Dockerfile bạn sử dụng: EXPOSE 8080 có nghĩa là để chạy được ứng dụng trong container thì với cú pháp docker run -p [port_in_server]:[port_in_container] [image] thì bạn sẽ chạy là docker run -p 8085:8080 java-backend, như vậy chúng ta chỉ có thể sử dụng port 8080 đã được expose thôi.
8. Kiểm soát thông tin xác thực và bảo mật
Không lưu trữ thông tin mật hoặc thông xác thực trong Dockerfile, không sử dụng các biến môi trường, tham số hoặc hard code để lưu trữ thông tin mật trong các lệnh của Dockerfile. Mình thấy có bạn cũng “để đó cho tiện” và dẫn đến lộ rất nhiều thông tin cần bảo mật.
Cẩn thận với các file được sao chép vào container, ngay cả khi bạn xóa file đó trong một lệnh sau đó ở Dockerfile thì file đó vẫn có thể truy cập và lấy được thông tin ở các layer trước đó của image. Vì vậy khi build image cần tuân thủ theo các bước như sử dụng các biến môi trường, sử dụng tệp cấu hình và bind mount.
Ví dụ:
Dockerfile không an toàn:
FROM node:18
WORKDIR /app
COPY . .
ENV DB_PASSWORD=passdevopseduvn
RUN npm install
CMD ["node", "server.js"]
Dockerfile an toàn hơn:
FROM node:18 WORKDIR /app COPY . . RUN npm install CMD ["node", "server.js"]
Khi chạy container:
9. Không chạy container với người dùng root
Người dùng root là người dùng có quyền cao nhất của hệ thống nên chúng ta sẽ sử dụng phương pháp Rootless Container để hạn chế các đặc quyền không cần thiết đối với container. Rootless Container có nghĩa là container được run bởi người dùng non-root đồng thời chạy toàn bộ ứng dụng trong container cũng với người dùng không có đặc quyền root. Để chạy container với người dùng khác root, ta cho phép người dùng đó truy cập /var/run/docker.sock bằng cách thêm người dùng vào group docker bằng lệnh:FROM node:18 WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN adduser -D manhnv && chown -R manhnv /app USER manhnv EXPOSE 3000 CMD ["node", "server.js"
10. Quét bảo mật image
Sử dụng công cụ quét image để phát hiện các lỗ hổng và đảm bảo rằng image của bạn không gặp rủi ro bảo mật trước khi triển khai. Những công cụ này có thể được tích hợp vào quy trình CI/CD để tự động quét image trước khi chúng được build và deploy, đảm bảo rằng chỉ những image an toàn mới được sử dụng trong môi trường Production.
Các công cụ quét rất phổ biến mà hầu hết các doanh nghiệp sử dụng hiện nay là:
- Astra Security: Trình quét lỗ hổng của Astra Security cung cấp tính năng quét bảo mật tự động và thủ công cho các container dựa trên cloud
- Anchore: Anchore là một công cụ nguồn mở cung cấp khả năng phân tích container image và đánh giá dựa trên chính sách. Công cụ này kiểm tra container image theo các chính sách có thể tùy chỉnh và có thể cung cấp thông báo khi phát hiện vi phạm chính sách.
- Trivy: Trivy là một công cụ quét image mã nguồn mở được thiết kế nhẹ và nhanh. Công cụ này kiểm tra các container image để tìm các lỗ hổng đã được phát hiện trong cả package của hệ điều hành và các thành phần phụ thuộc của chúng.
- Aqua Security: Aqua Security cung cấp nền tảng bảo mật container bao gồm công cụ quét image. Công cụ này kiểm tra container image để tìm các lỗ hổng đã được phát hiện, phần mềm độc hại và các moosid de dọa bảo mật khác.
Việc lựa chọn sử dụng công nghệ nào sẽ tùy thuộc vào từng doanh nghiệp nhưng thực tế bạn có thể thấy thống kê trivy là một công cụ được sử dụng rất rộng rãi vì tính dễ dàng và miễn phí của nó.
11. Ngăn chặn container tăng đặc quyền
Các container thường có thể tăng đặc quyền của mình bằng cách gọi các nhị phân setuid và setgid. Điều này là một rủi ro bảo mật vì quá trình trong container có thể sử dụng setuid để trở thành root.
Để ngăn chặn điều này, bạn nên đặt tùy chọn bảo mật no-new-privileges khi khởi động container của mình:
12. Kiểm soát quyền tác động vào registry
Registry là một phần cực kỳ quan trọng lưu trữ docker image. Nên việc giới hạn các quyền cụ thể dự án nào người dùng đó sẽ giúp bạn hạn chế tối đa thiệt hại khi chẳng may có ai đó bị lộ tài khoản.
Trong dự án lại phân định rất rõ ràng, ai có quyền pull, push image lên registry để đảm bảo rằng khi có vấn đề sẽ xác định được ngay ai có trách nhiệm về việc đó.
13. Bảo mật mạng truy cập và giám sát bảo mật container
Triển khai các biện pháp kiểm soát bảo mật mạng để ngăn chặn truy cập trái phép vào container. Sử dụng phân đoạn mạng để cách ly các container và giới hạn quyền truy cập mạng chỉ vào các dịch vụ cần thiết.
Giám sát logs và metrics để phát hiện các điểm bất thường và ứng phó với các sự cố bảo mật. Triển khai giám sát container để phát hiện hoạt động đáng ngờ và các vi phạm bảo mật tiềm ẩn.
Bạn có thể sử dụng các công cụ giám sát container phổ biến như Prometheus và Grafana để giám sát hiệu suất và hoạt động của các container đang chạy trong cụm của bạn. Những công cụ này có thể giúp bạn phát hiện các vấn đề về sử dụng tài nguyên, hiệu suất ứng dụng và các số liệu quan trọng khác.
Sử dụng các công cụ giám sát mạng như Wireshark hoặc tcpdump để nắm bắt và phân tích lưu lượng mạng giữa các container. Điều này giúp bạn phát hiện các mối đe dọa bảo mật tiềm ẩn hoặc các vấn đề về hiệu suất.
Trên đây là các phương pháp thực tế để có thể bảo mật container. devopsedu.vn với mục tiêu cung cấp cho cộng đồng những kiến thức DevOps thực tế và chi tiết.
Nếu có bất kỳ góp ý nào vui lòng truy cập trang liên hệ hoặc gửi mail trực tiếp vào địa chỉ contact@devopsedu.vn.