Certified Kubernetes Administrator (CKA) with Practice Tests - Phần 6: Security

Phan Văn HoàngPhan Văn Hoàng
30 min read

Kubermetes Security Primitives

Kubernetes là một platform phù hợp để lưu trữ các ứng dụng bằng container. Security là mối quan tâm hàng đầu.

Tất cả quyền truy cập vào các máy chủ phải được bảo mật:

  1. Xác thực dựa trên mật khẩu bị vô hiệu hóa.

  2. Chỉ cung cấp xác thực dựa trên SSH Key.

Và tất nhiên, bất kỳ biện pháp nào khác mà bạn cần thực hiện để đảm bảo an toàn cho cơ sở hạ tầng phần cứng hoặc máy ảo dùng để triển khai và vận hành Kubernetes. Nếu điều đó bị xâm nhập thì mọi thứ đều bị xâm nhập.

Kube-apiserver nằm ở trung tâm của tất cả các hoạt động trong Kubernetes, tương tác với nó thông qua tiện ích kubectl hoặc bằng các truy cập trực tiếp vào API. Và thông qua đó bạn có thể thực hiện hầu hết mọi thao tác trên cluster, vì vậy đó là tuyến phòng thủ (defense) đầu tiên, kiểm soát quyền truy cập vào API server.

💡
Ai có thể truy cập vào cluster và có thể làm gì?

Ai có thể truy cập vào API server được xác định bởi cơ chế xác thực, có nhiều cách khác nhau để có thể xác thực tới API server.

  • Username và password

  • Username và token

  • Certìicates

  • LDAP

  • Service Accounts

Khi họ có quyền truy cập vào cluster, những gì họ có thế làm được xác thực bởi cơ chế ủy quyền:

  • RBAC Authorization

  • ABAC Authorization

  • Node Authorization

  • Webhook Mode

Tất cả thông tin liên lạc với cluster, giữa các thành phần khác nhau như etcd cluster, kube-controller-manager, scheduler, ApiServer cũng như những thành phần chạy trên worker node chẳng hạn kubelet, kube-proxy đươc bảo mật bằng TLS encryption.

💡
Còn giao tiếp giữa các ứng dụng trong cluster thì sao?

Theo mặc định, tất cả các pod đều có thể truy cập vào tất cả các pod khác trong cluster. Bây giờ bạn có thể hạn chế quyền truy cập giữa chúng sử dụng network policies.

Authentication

Cluster Kubernetes bao gồm nhiều nodes, physicals hoặc virtual.

  • Admins truy cập vào cluster để thực hiện nhiệm vụ quản trị.

  • Developers truy cập vào cluster để kiểm tra hoặc triển khai ứng dụng.

  • End Users truy cập vào ứng dụng được triển khai trên cluster.

  • Bots (ứng dụng bên thứ 3) truy cập vào cluster cho mục đích tích hợp.

Đảm bảo liên lạc giữa các thành phần bên trong và đảm bảo quyền truy cập quản lý cluster thông qua cơ chế xác thực và ủy quyền.

Chúng ta đã nói về những người dùng khác nhau có thể truy cập vào cluster:

Đối với End Users là những người truy cập ứng dụng được triển khai trên cụm được quản lý bởi chính ứng dụng.Trọng tâm là quyền truy cập của người dùng vào cụm Kubernetes cho mục đính quản trị. Vậy còn lại hai loại người dùng là User và Service Account:

Kubernetes không quản lý user một cách tự nhiên, nó dựa vào một nguồn bên ngoài giống như một tệp có các thông tin chi tiết hoặc certificates hoặc dịch vụ bên thứ ba như LDAP để quản lý những người dùng này.

Do đó bạn không thể tạo user trong cluster hoặc xem các user như thế này:

Tuy nhiên với service accounts, kubernetes có thể quản lý chúng. Bạn có thể tạo vào quản lý các service accounts sử dụng Apiserver:

Tất cả quyền truy cập của user được quản lý bởi ApiServer, cho dù bạn có quyefn truy cập vào cluster thông qua kubectl hoặc qua API thì tất cả các yêu cầu này đều đi qua kube-apiserver:

Kube-apiserver xác thực người dùng trước khi xử lý nó.

💡
Vậy kube-apiserver xác thực như thế nào?

Có nhiều cơ chế xác thực khác nhau có thể được cấu hình như:

  • Static Password File

  • Static Token File

  • Certificates

  • Identity Services

Đối với Static Password File & Static Token File, bạn có thể tạo danh sách users và password của họ trong tệp csv và sử dụng file đó làm thông tin người dùng.

password123,user1,u0001
password123,user2,u0002
password123,user3,u0003
password123,user4,u0004
password123,user5,u0005

File gồm có 3 cột: password, username, user_id. Sau đó chuyển tệp này dưới dạng option đến kube-apiserver, sau đó restart lại kube-apiserver để các thay đổi này có hiệu lực.

--basic-auth-file=user-details.csv
apiVersion: v1
kind: Pod
metadata:
    creationTimestamp: null
    name: kube-apiserver
    namespace: kube-system
spec:
    containers:
    - command:
    - kube-apiserver
    - --authorization-mode=Node,RBAC
    - --advertise-address=172.17.0.107
    - --allow-privileged=true
    - --enable-admission-plugins=NodeRestriction
    - --enable-bootstrap-token-auth=true
    image: k8s.gcr.io/kube-apiserver-amd64:v1.11.3
    name: kube-apiserver

Để xác thực thông tin người dùng trong khi truy cập ApiServer, chỉ định username và password như này:

$ curl -v -k https://master-node-ip:6443/api/v1/pods -u "user1:password123"
{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {
    "selfLink": "/api/v1/pods",
    "resourceVersion": "3594"
},
"items": [
{
    "metadata": {
    "name": "nginx-64f497f8fd-krkg6",
    "generateName": "nginx-64f497f8fd-",
    "namespace": "default",
    "selfLink": "/api/v1/namespaces/default/pods/nginx-64f497f8fd-krkg6",
    "uid": "77dd7dfb-2914-11e9-b468-0242ac11006b",
    "resourceVersion": "3569",
    "creationTimestamp": "2019-02-05T07:05:49Z",
    "labels": {
        "pod-template-hash": "2090539498",
        "run": "nginx" 
            }
    }
}

Chúng ta có thể thêm cột thứ 4: group vào file user để phân công user vào các nhóm cụ thể:

KpjCVbI7rCFAHYPkByTIzRb7gu1cUc4B,user10,u0010,group1
rJjncHmvtXHc6MlWQddhtvNyyhgTdxSC,user11,u0011,group1
mjpOFIEiFOkL9toikaRNtt59ePtczZSq,user12,u0012,group2
PG41IXhs7QjqwWkmBkvgGT9glOyUqZij,user13,u0013,group2

Truyền tệp dưới dạng option tới kube-apiserver:

--token-auth-file=user-token-details.csv

Để xác thực thông tin người dùng trong khi truy cập ApiServer, chỉ định token như này:

curl -v -k https://master-node-ip:6443/api/v1/pods ip:6443/api/v1/pods \
        --header "Authorization: Bearer KpjCVbI7rCFAHYPkBzRb7gu1cUc4B"

TLS Basics

Certificate được sử dụng để đảm bảo sự tin cậy giữa hai bên trong quá trình giao dịch.

Ví dụ:

Người dùng cố gắng truy cập vào web server, certificates TLS đảm bảo rằng việc liên lạc giữa người dùng và server được mã hóa và server chính là người được thông báo.

Nếu không có kết nối an toàn, nếu người dùng truy cập vào ứng dụng Online banking thông tin xác thực người dùng nhập vào sẽ được gửi ở định dạng plaintext.

Hacker đang nghe lén lưu lượng mạng, có thể dễ dàng lấy thông tin đăng nhập và sử dụng nó để hack vào tài khoản ngân hàng của người dùng. Điều đó rõ ràng không an toàn, vì vậy bạn phải mã hóa dữ liệu đang được truyền bằng cách sử dụng các khóa mã hóa.

Dữ liệu được mã hóa bằng khóa, về cơ bản là một tập hợp các số và chữ số ngẫu nhiên và bạn mã hóa nó thành một định dạng không thể nhận dạng được.

Dữ liệu sau đó được gửi đến server, hacker đang nghe lén lưu lượng mạng lấy được dữ liệu nhưng không thể làm gì với nó. Tuy nhiên trường hợp này cũng tương tự khi server nhận dữ liệu, nó không thể giải mã dũ liệu nếu không có chìa khóa.

Vì vậy, một bản sao của khóa cũng phải được gửi đến server để server có thể giải mã. Vì khá cũng được gửi qua cùng một mạng nên hacker cũng thế lấy được nó và giải mã dữ liệu.

Điều này được gọi là mã hóa đối xứng, đó là một cách mã hóa an toàn nhưng vì nó sử dụng một khóa để mã hóa và giải mã và vì khóa phải được trao đổi giữa người gửi và người nhận có nguy cơ hacker có thể truy cập vào khóa và giải mã dữ liệu. Đó là lúc mã hóa bất đối xứng xuất hiện.

Thay vì sử dụng một khóa duy nhất để mã hóa và giải mã, mã hóa bất đối xứng sử dụng một cặp khóa private key & public key. Khóa chỉ thuộc về tôi nên nó private, một khóa mà ai cũng có thể truy cập gọi là public.

Nếu bạn mã hóa dữ liệu bằng public key thì bạn chỉ có thể giải mã nó bằng private key. Vì vậy private key luôn phải an toàn và không chia sẻ với bất kỳ ai khác nhưng public key có thể được chia sẻ với người khác. Nhưng họ chỉ có thể mã hóa thứ gì đó với nó, bất kỳ cái gì bị khóa bằng public key nó chỉ có thể được giải mã bằng private key.

Sử dụng cặp khóa private key & public key đảm bảo quyền truy cập SSH vào server. Bạn có một server mà bạn cần truy cập, bạn không muốn sử dụng mật khẩu vì chúng quá rủi ro vì vậy bạn quyết định sử dụng cặp khóa.

Bạn tạo một cặp khóa private key & public key, bạn có thể thực hiện việc này bằng cách chạy lệnh:

$ ssh-keygen
id_rsa    id_rsa.pub

Sau đó bạn chuyển id_rsa.pub (public key) vào server, việc này thường được thực hiện bằng cách thêm một tệp vào tệp /.ssh/authorized_keys. Khi bạn cố gắng SSH vào server, chỉ định vị trí private_key trong lệnh SSH.

Điều gì xảy ra nếu bạn có các server khác, làm cách nào để truy cập nhiều máy chủ bằng cặp khóa của bạn?

Bạn có thể tạo bản sao public key và đặt chúng lên server tùy thích, bạn có thể sử dụng một private key để SSH vào tất cả các server một cách an toàn.

💡
Điều gì xảy ra nếu người khác cần quyền truy cập vào server?

Thực hiện tạo cặp khóa private & public, sau đó thực hiện thêm file public key vào server và sử dụng private key để truy cập SSH vào server.

Để giải quyết vấn đề mã hóa dữ liệu qua đường truyền, chúng ta tạo một cặp khóa private & public trên server với lệnh:

$ openssl genrsa -out my-bank.key 1024
my-bank.key

$ openssl rsa -in my-bank.key -pubout > mybank.pem
mybank.pem

Khi người dùng lần đàu truy cập server bằng HTTPS, người dùng nhận được public_key từ server. Vì hacker đang nghe lén lưu lượng mạng nên hacker cũng nhận được một public_key đấy.

Sau đó người dùng thực hiện mã hóa khóa đối xứng bằng public key và gửi lên server, hacker cũng nhận được một bản sao. Máy chủ sử dụng private key để giải mã thông điệp và lấy khóa đối xứng từ nó tuy nhiên hacker không có private key để giải mã để lấy đươc khóa đối xứng. Hacker chỉ có public key nên không giải mã được thông điệp.

Khóa đối xứng hiện tại an toàn chỉ có server biết được, bây giờ có thể sử dụng khóa đối xứng để mã hóa dữ liệu và gửi cho nhau. Người nhận có thể sử dụng một khóa đối xứng để giải mã dữ liệu và lấy thông tin, hacker không thể giải mã bất kỳ dữ liệu nào.

Với mã hóa bất đối xứng, chúng tôi đã chuyển thành công các khóa đối xứng từ người dùng đến server và với mã hóa đối xứng chúng tôi đã đảm bảo tất cả thông tin liên lạc giữa họ.

TLS in Kubernetes

Có 3 loại certificate chính:

  1. Server certificates: được cấu hình trên server.

  2. Root certificates: được cấu hình trên server CA.

  3. Client certificates: được xấu hình trên client.

Quy ước đặt tên:

Cluster kubernetes bao gồm một tập hợp các master node và worker node. Tất nhiên, tất cả các thông tin liên lạc giữa các node này cần phải được bảo mật và phải được mã hóa. Tất cả các tương tác giữa các dịch vụ và client cần được an toàn.

Ví dụ:

Quản trị viên tương tác với cụm Kubernetes thông qua kubectl hoặc truy cập trực tiếp vào API Kubernetes phải thiết lập kết nối TLS an toàn. Giao tiếp giữa tất cả các thành phần trong cụm cũng cần được bảo mật.

Vì vậy, hai yêu cầu chính là:

  1. Các dịch vụ trong cụm phải sử dụng server certificates.

  2. Các máy khách phải sử dụng client certificates để xác thực danh tính.

Server Certificates for Servers

Kube-apiserver

Đây là máy chủ HTTPS mà các thành phần khác trong cụm cũng như người dùng bên ngoài sử dụng để quản lý cụm Kubernetes. Vì vậy nó là một máy chủ và nó yêu cầu certificates để đảm bảo mọi thông tin liên lạc với khách hàng của mình. Nên đã tạo ra một cặp chứng chỉ: apiserver.crt & apiserver.key

ETCD Server

Đây là máy chủ lưu trữ tất cả các thông tin về cụm vì vậy nó yêu cầu một cặp chứng chỉ: etcdserver.crt & etcdserver.key

Kubelet Server

Dịch vụ trên các worker nodes, cung cấp điểm cuối HTTPS để giao tiếp với kube-apiserver. Nên đã tạo ra một cặp chứng chỉ: kubelet.crt & kubelet.key

Client Certificates for Clients

Admin (Quản trị viên)

Sử dụng kubectl hoặc API REST để tương tác với kube-apiserver, yêu cầu cặp chứng chỉ admin.crt & admin.key

Kube-Scheduler

Truy cập kube-apiserver để lập lịch cho các pod. Scheduler là một client truy cập vào kube-apiserver, đối với kube-apiserver thì scheduler là một ứng dụng client giống như quản trị viên, yêu cầu cặp chứng chỉ scheduler.crt & scheduler.key

Kube-Controller-Manager

Truy cập kube-apiserver để thực hiện các tác vụ quản lý cụm, yêu cầu cặp chứng chỉ controller-manager.crt & controller-manager.key

Kube-Proxy

Yêu cầu chứng chỉ client để xác thực với kube-apiserver,vì vậy nó yêu cầu cặp chứng chỉ kube-proxy.crt & kube-proxy.key

Trên thực tế, trong tất cả các thành phần trong cụm kubernetes thì kube-apiserver là máy chủ duy nhất giao tiếp với etcd server. Đối với etcd server thì kube-apiserver là một client nên nó cần được xác thực. Kube-apiserver có thể dùng lại chứng chỉ APIserver.crtAPIserver.key hoặc tạo mới.

Kube-apiserver giao tiếp với kubelet trên các worker nodes để giám sát chúng, nó có thể sử dụng chứng chỉ gốc hoặc tạo mới.

Client Certificates: Được sử dụng bởi các máy khách để kết nối với kube-apiserver.

Server Certificates: Được sử dụng bởi kube-apiserver, etcd server, và kubelet để xác thực máy khách.

Certificates API

Với tư cách là quản trị viên của cluster, trong quá trình thiết lập cluster đã thiết lập server CA và các certificates cho các component khác.

Tôi là quản trị viên và là người dùng duy nhất trong cluster và có certificate, key admin. Một quản trị viên mới gia nhập và cần quyền truy cập vào cluster. Chúng ta cần đưa cho quản trị viên mới một cặp certificate, key để có thể truy cập vào cluster.

Quản trị viên mới tạo private_keyCSR (Certificate singing request) gửi nó cho tôi, sau đó tôi đưa CSR tới server CA và được server CA ký sử dụng private_key & root certificate từ đó tạo ra một certificate rồi gửi lại cho người quản trị viên mới.

Bây giờ, quản trị viên mới có một cặp certificate hợp lệ và private key của riêng mình để có thể truy cập vào cluster. Các certificate chỉ có thời gian hiệu lực và nó kết thúc sau một khoảng thời gian, mỗi lần hết hạn thì phải thực hiện theo quy trình tương tự để tạo CSR mới và nhận được chữ ký của CA.

💡
CA server là gì? Nằm ở dâu trong kubernetes?

CA thực chất chỉ là một cặp private_keycertificate mà chúng ta đã tạo ra. Bất kỳ ai có quyền truy cập vào cặp tệp này đều có thể ký bất kỳ chứng chỉ nào trong môi trường Kubernetes. Họ có thể tạo ra số lượng người dùng tùy ý với bất kỳ quyền hạn nào mà họ muốn.

Vì vậy những tập tin này cần được bảo vệ và được lưu trữ trong môi trường an toàn, giả sử chúng ta đặt chúng trên một server hoàn toàn an toàn. Bây giờ server đó trở thành server CA. Sau đó private_keycertificate được lưu trữ an toàn trong server đó và chỉ trên server đó có. Mỗi lần bạn muốn ký một certificate, bạn chỉ có thể làm điều đó bằng cách đăng nhập vào server.

Hiện tại, các chứng chỉ được lưu trữ trực tiếp trên node master của Kubernetes. Vì vậy, node master cũng đóng vai trò như máy chủ CA (CA server). Công cụ Kubeadm cũng làm điều tương tự, nó tạo ra một cặp tệp CA và lưu trữ trực tiếp trên node master.

Cho đến nay, chúng ta đã thực hiện ký yêu cầu một cách thủ công, nhưng khi số lượng người dùng tăng lên và đội ngũ phát triển lớn mạnh hơn, bạn sẽ cần một phương pháp tự động hóa tốt hơn để quản lý các yêu cầu ký chứng chỉ, cũng như luân chuyển chứng chỉ khi chúng hết hạn.

Kubernetes có sẵn một Certificates API để hỗ trợ bạn thực hiện điều này. Với Certificates API, bạn có thể gửi yêu cầu ký chứng chỉ (Certificate Signing Request) trực tiếp đến Kubernetes thông qua một API call. Lần này, khi quản trị viên nhận được yêu cầu ký chứng chỉ, thay vì phải đăng nhập vào node master và tự mình ký chứng chỉ, quản trị viên có thể xử lý thông qua API.

Quản trị viên sẽ tạo một đối tượng API của Kubernetes gọi là CertificateSigningRequest. Khi đối tượng này được tạo, tất cả các yêu cầu ký chứng chỉ sẽ có thể được các quản trị viên của cluster xem xét. Yêu cầu có thể được xem xétphê duyệt dễ dàng bằng các lệnh kubectl. Sau đó, chứng chỉ đã được ký có thể được trích xuất và chia sẻ với người dùng.

Đầu tiên người dùng sẽ tạo một private_key:

$ openssl genrsa -out jane.key 2048
jane.key

Sau đó, người dùng tạo một yêu cầu ký chứng chỉ (CSR) bằng khóa riêng tư, trong đó chứa tên của họ.

$ openssl req -new -key jane.key -subj "/CN=jane" -out jane.csr
jane.csr
-----BEGIN CERTIFICATE REQUEST-----
MIICWDCCAUACAQAwEzERMA8GA1UEAwwIbmV3LXVzZXIwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDO0WJW+DXsAJSIrjpNo5vRIBplnzg+6xc9+UVwkKi0
LfC27t+1eEnON5Muq99NevmMEOnrDUO/thyVqP2w2XNIDRXjYyF40FbmD+5zWyCK
9w0BAQsFAAOCAQEAS9iS6C1uxTuf5BBYSU7QFQHUzalNxAdYsaORRQNwHZwHqGi4
hOK4a2zyNyi44OOijyaD6tUW8DSxkr8BLK8Kg3srREtJql5rLZy9LRVrsJghD4gY
P9NL+aDRSxROVSqBaB2nWeYpM5cJ5TF53lesNSNMLQ2++RMnjDQJ7juPEic8/dhk
Wr2EUM6UawzykrdHImwTv2mlMY0R+DNtV1Yie+0H9/YElt+FSGjh5L5YUvI1Dqiy
4l3E/y3qL71WfAcuH3OsVpUUnQISMdQs0qWCsbE56CC5DhPGZIpUbnKUpAwka+8E
vwQ07jG+hpknxmuFAeXxgUwodALaJ7ju/TDIcw==
-----END CERTIFICATE REQUEST-----

Quản trị viên sẽ lấy yêu cầu ký chứng chỉ (CSR) và tạo một đối tượng CertificateSigningRequest. Đối tượng CertificateSigningRequest được tạo giống như bất kỳ đối tượng Kubernetes nào khác, sử dụng một tệp manifest với các trường thông tin thông thường.

$ cat jane.csr | bas64
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ1dEQ0NBVUFDQVFB
d0V6RVJNQThHQTFVRUF3d0libVYzTFhWelpYSXdnZ0VpTUEwR0NTcUdTSWIzRFFFQgpB
UVVBQTRJQkR3QXdnZ0VLQW9JQkFRRE8wV0pXK0RYc0FKU0lyanBObzV2UklCcGxuemcr
NnhjOStVVndrS2kwCkxmQzI3dCsxZUVuT0
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
    name: jane
spec:
    groups:
    - system:authenticated
usages:
    - digital signature
    - key encipherment
    - server auth
    request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ1dEQ0NBVUFDQVFB
    d0V6RVJNQThHQTFVRUF3d0libVYzTFhWelpYSXdnZ0VpTUEwR0NTcUdTSWIzRFFFQgpB
    UVVBQTRJQkR3QXdnZ0VLQW9JQkFRRE8wV0pXK0RYc0FKU0lyanBObzV2UklCcGxuemcr
    NnhjOStVVndrS2kwCkxmQzI3dCsxZUVuT0

Tất cả các yêu cầu ký chứng chỉ có thể được các quản trị viên xem bằng cách chạy lệnh:

$ kubectl get csr
NAME     AGE         REQUESTOR             CONDITION
jane     10m         admin@example.com     Pending

Xác định yêu cầu mới và phê duyệt yêu cầu đó bằng cách chạy lệnh:

$ kubectl certificate approve jane
jane approved!

Kubernetes ký chứng chỉ bằng cách sử dụng cặp khóa của CA và tạo ra chứng chỉ cho người dùng. Chứng chỉ này sau đó có thể được trích xuất và chia sẻ với người dùng.

Xem chứng chỉ bằng cách hiển thị nó dưới định dạng YAML. Chứng chỉ được tạo ra là một phần của đầu ra, nhưng như trước, nó ở định dạng mã hóa Base64.

apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
    creationTimestamp: 2019-02-13T16:36:43Z
    name: new-user
spec:
    groups:
    - system:masters
    - system:authenticated
usages:
    - digital signature
    - key encipherment
    - server auth
    username: kubernetes-admin
status:
    certificate:
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURDakNDQWZLZ0F3SUJBZ0lVRmwy
Q2wxYXoxaWl5M3JNVisreFRYQUowU3dnd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZURVRN
QkVHQTFVRUF4TUthM1ZpWlhKdVpYUmxjekFlRncweE9UQXlNVE14TmpNeU1EQmFGd1dn
Y0ZFeDl2ajNuSXY3eFdDS1NIRm5sU041c0t5Z0VxUkwzTFM5V29GelhHZDdWCmlEZ2FO
MVVRMFBXTVhjN09FVnVjSWc1Yk4weEVHTkVwRU5tdUlBNlZWeHVjS1h6aG9ldDY0MEd1
MGU0YXFKWVIKWmVMbjBvRTFCY3dod2xic0I1ND0KLS0tLS1FTkQgQ0VSVElGSUNBVEUt
LS0tLQo=
    conditions:
    - lastUpdateTime: 2019-02-13T16:37:21Z
      message: This CSR was approved by kubectl certificate approve.
      reason: KubectlApprove
      type: Approved

Để giải mã nó, bạn chỉ cần lấy văn bản và sử dụng tùy chọn giải mã của công cụ Base64. Điều này sẽ trả về chứng chỉ ở định dạng văn bản thuần (plaintext). Sau đó, chứng chỉ có thể được chia sẻ với người dùng cuối.

$ echo "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ1dEQ0NBVUFDQVFB
d0V6RVJNQThHQTFVRUF3d0libVYzTFhWelpYSXdnZ0VpTUEwR0NTcUdTSWIzRFFFQgpB
UVVBQTRJQkR3QXdnZ0VLQW9JQkFRRE8wV0pXK0RYc0FKU0lyanBObzV2UklCcGxuemcr
NnhjOStVVndrS2kwCkxmQzI3dCsxZUVuT0" | base64 --decode
-----BEGIN CERTIFICATE REQUEST-----
MIICWDCCAUACAQAwEzERMA8GA1UEAwwIbmV3LXVzZXIwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDO0WJW+DXsAJSIrjpNo5vRIBplnzg+6xc9+UVwkKi0
LfC27t+1eEnON5Muq99NevmMEOnrDUO/thyVqP2w2XNIDRXjYyF40FbmD+5zWyCK
9w0BAQsFAAOCAQEAS9iS6C1uxTuf5BBYSU7QFQHUzalNxAdYsaORRQNwHZwHqGi4
hOK4a2zyNyi44OOijyaD6tUW8DSxkr8BLK8Kg3srREtJql5rLZy9LRVrsJghD4gY
P9NL+aDRSxROVSqBaB2nWeYpM5cJ5TF53lesNSNMLQ2++RMnjDQJ7juPEic8/dhk
Wr2EUM6UawzykrdHImwTv2mlMY0R+DNtV1Yie+0H9/YElt+FSGjh5L5YUvI1Dqiy
4l3E/y3qL71WfAcuH3OsVpUUnQISMdQs0qWCsbE56CC5DhPGZIpUbnKUpAwka+8E
vwQ07jG+hpknxmuFAeXxgUwodALaJ7ju/TDIcw==
-----END CERTIFICATE REQUEST-----

KubeConfig

Cho đến nay, chúng ta đã tìm hiểu cách tạo chứng chỉ cho một người dùng. Bạn đã thấy cách một client sử dụng tệp chứng chỉ và khóa để truy vấn Kubernetes REST API nhằm lấy danh sách các pod bằng curl.

$ curl https://my-kube-playground:6443/api/v1/pods \
    --key admin.key
    --cert admin.crt
    --cacert ca.crt

{
    "kind": "PodList",
    "apiVersion": "v1",
    "metadata": {
        "selfLink": "/api/v1/pods",
    },
    "items": []
}

Trong trường hợp này, cluster của tôi được gọi là my-kube-playground, vì vậy chúng ta sẽ gửi một yêu cầu cURL tới địa chỉ của kube-apiserver, đồng thời truyền các tệp chứng chỉ bearer cùng với chứng chỉ CA dưới dạng tùy chọn. Sau đó, API server sẽ xác thực người dùng dựa trên các thông tin này.

💡
Vậy làm thế nào để thực hiện điều đó bằng cách sử dụng lệnh kubectl?

Bạn có thể chỉ định cùng một thông tin bằng cách sử dụng các tùy chọn --server, --client-key, --client-certificate, và --certificate-authority với công cụ kubectl.

$ kubectl get pods
        --server my-kube-playground:6443
        --client-key admin.key
        --client-certificate admin.crt
        --certificate-authority ca.crt

No resources found.

Rõ ràng, việc nhập những thông tin này mỗi lần thực thi lệnh là một công việc tẻ nhạt, vì vậy bạn sẽ chuyển những thông tin này vào một tệp cấu hình gọi là kubeconfig. Sau đó, chỉ cần chỉ định tệp này bằng tùy chọn --kubeconfig trong lệnh của bạn.

Theo mặc định, công cụ kubectl sẽ tìm một tệp có tên là config trong thư mục .kube nằm dưới thư mục chính (home) của người dùng. Vì vậy, nếu bạn lưu tệp kubeconfig ở đó, bạn không cần phải chỉ định đường dẫn đến tệp một cách rõ ràng trong lệnh kubectl. Đó là lý do tại sao bạn chưa cần chỉ định bất kỳ tùy chọn nào cho các lệnh kubectl của mình.

Tệp kubeconfig có một định dạng cụ thể. Tệp cấu hình này bao gồm ba phần chính: clusters, users, và contexts.

Clusters đại diện cho các cluster Kubernetes khác nhau mà bạn cần truy cập.

Ví dụ:

Bạn có thể có nhiều cluster dành cho môi trường phát triển, môi trường kiểm thử, môi trường sản xuất, hoặc cho các tổ chức khác nhau, hoặc trên các nhà cung cấp đám mây khác nhau, v.v. Tất cả các cluster đó sẽ được liệt kê trong phần clusters.

Users là các tài khoản người dùng mà bạn sử dụng để truy cập vào những cluster này.

Ví dụ:

Người dùng admin, người dùng dev, người dùng prod, v.v. Những người dùng này có thể có các quyền khác nhau trên từng cluster.

Cuối cùng, contexts kết hợp tất cả những phần trên lại với nhau. Contexts xác định tài khoản người dùng nào sẽ được sử dụng để truy cập vào cluster nào.

Ví dụ:

Bạn có thể tạo một context tên là admin@production, sử dụng tài khoản admin để truy cập vào cluster production. Hoặc bạn có thể muốn truy cập vào cluster đã thiết lập trên Google bằng thông tin đăng nhập của người dùng dev để kiểm tra việc triển khai ứng dụng mà bạn đã xây dựng.

Tệp kubeConfig có định dạng file YAML:

apiVersion: v1
kind: Config

current-context: my-kube-admin@my-kube-playground
clusters:
- name: my-kube-playground
  cluster:
    certificate-authority: ca.crt
    server: http://my-kube-playground:6443
- name: my-kube-development
  cluster:
    certificate-authority: ca.crt
    server: http://my-kube-development:6443
contexts:
- name: my-kube-admin@my-kube-playground
  context:
    cluster: my-kube-playground
    user: my-kube-admin
- name: my-kube-dev@my-kube-development
  context:
    cluster: my-kube-development
    user: my-kube-dev
users:
- name: my-kube-admin
  user:
    client-certificate: admin.crt
    client-key: admin.key
- name: my-kube-dev
  user:
    client-certificate: dev.crt
    client-key: dev.key

Để xem tệp cấu hình hiện tại đang được sử dụng, hãy chạy lệnh:

$ kubectl config view

Lệnh này sẽ liệt kê các context và người dùng của cluster, cũng như context hiện tại đã được thiết lập. Nếu bạn không chỉ định tệp kubeconfig nào để sử dụng, kubectl sẽ tự động sử dụng tệp mặc định nằm trong thư mục .kube trong thư mục chính (home) của người dùng.

Ngoài ra, bạn có thể chỉ định tệp kubeconfig cụ thể bằng cách tùy chọn --kubeconfig trong dòng lệnh như sau:

$ kubectl config view --kubeconfig=/path/to/your/kubeconfig

Để thay đổi context và sử dụng người dùng my-kube-dev để truy cập vào my-kube-development cluster, bạn có thể chạy lệnh sau:

$ kubectl config use-context my-kube-dev@my-kube-development

API Groups

API của Kubernetes được nhóm thành nhiều nhóm khác nhau dựa trên mục đích sử dụng, chẳng hạn như một nhóm cho APIs, một nhóm cho sức khỏe (health), một nhóm cho các chỉ số và nhật ký (metrics và logs), v.v. API Metrics và Health được sử dụng để giám sát sức khỏe của cluster. Các nhật ký (logs) được sử dụng để tích hợp với các ứng dụng nhật ký bên thứ ba.

Các API này được phân loại thành hai nhóm: core & named.

Nhóm core là nơi chứa tất cả các chức năng cốt lõi, chẳng hạn như tên, không gian (namespaces), pods, replication controllers, events và points, nodes, bindings, persistent volumes, persistent volume claims, config maps, secrets, services, v.v.

Nhóm named được tổ chức tốt hơn và trong tương lai, tất cả các tính năng mới sẽ được cung cấp thông qua các nhóm có tên này. Nó bao gồm các nhóm con như apps, extensions, networking, storage, authentication, authorization, v.v.

Trong nhóm apps, bạn có các tài nguyên như deployments, replica sets, stateful sets. Trong nhóm networking, bạn có network policies. Nhóm certificates có các yêu cầu ký chứng chỉ (certificate signing requests).

Vậy, các nhóm ở trên là API groups, và các nhóm ở dưới là resources trong những nhóm đó. Mỗi tài nguyên trong này có một tập hợp các hành động liên quan đến chúng; những thao tác mà bạn có thể thực hiện với các tài nguyên này, chẳng hạn như liệt kê các deployments, lấy thông tin về một deployment nào đó, tạo một deployment, xóa một deployment, cập nhật một deployment, theo dõi một deployment, v.v. Những hành động này được gọi là verbs.

Authorization

Là quản trị viên của cluster, chúng ta có thể thực hiện tất cả các loại thao tác trong đó, chẳng hạn như xem các đối tượng khác nhau như pods, nodes, và deployments, tạo hoặc xóa các đối tượng như thêm hoặc xóa pods hoặc thậm chí nodes trong cluster.

Là quản trị viên, chúng ta có thể thực hiện bất kỳ thao tác nào, nhưng sắp tới sẽ có những người khác truy cập vào cluster, chẳng hạn như các quản trị viên khác, nhà phát triển, kiểm thử viên hoặc các ứng dụng khác như ứng dụng giám sát hoặc các ứng dụng liên tục như Jenkins, v.v.

Vì vậy, chúng ta sẽ tạo tài khoản cho họ để truy cập vào cluster bằng cách tạo tên người dùng và mật khẩu, token, chứng chỉ TL đã ký, hoặc tài khoản dịch vụ. Tuy nhiên, chúng ta không muốn tất cả họ có cùng mức độ truy cập như chúng ta.

Ví dụ:

Chúng ta không muốn các nhà phát triển có quyền truy cập để thay đổi cấu hình cluster của chúng ta, như thêm hoặc xóa các nodes, cấu hình storage hoặc networking. Chúng ta có thể cho phép họ xem nhưng không sửa đổi, nhưng họ có thể có quyền truy cập để triển khai các ứng dụng.

Điều tương tự cũng áp dụng với các service accounts, chúng ta chỉ muốn cung cấp cho các ứng dụng bên ngoài mức độ quyền truy cập tối thiểu cần thiết để thực hiện các thao tác yêu cầu của chúng.

Khi chúng ta chia sẻ cluster giữa các tổ chức hoặc đội nhóm khác nhau, bằng cách phân vùng logic nó sử dụng namespaces, chúng ta muốn hạn chế quyền truy cập của người dùng chỉ vào các namespaces của họ. Đó chính là những gì authorization có thể giúp bạn trong cluster.

Kubernetes hỗ trợ các cơ chế authorization khác nhau, chẳng hạn như node authorization, attribute-based authorization, role-based authorizationwebhook.

Chúng ta biết rằng Kube API Server được truy cập bởi người dùng như chúng ta để quản lý, cũng như các kubelets trên các node trong cluster để thực hiện các quy trình quản lý trong cluster. Kubelet truy cập API Server để đọc thông tin về các services, points, nodes, và pods. Kubelet cũng báo cáo về API Server thông tin về node, chẳng hạn như trạng thái của nó. Những yêu cầu này được xử lý bởi một authorizer đặc biệt gọi là Node Authorizer.

Vì vậy, bất kỳ yêu cầu nào đến từ một người dùng có tên system node và là thành viên của nhóm system nodes đều được Node Authorizer ủy quyền và được cấp các quyền này, quyền này là cần thiết cho một kubelet. Đây là quyền truy cập bên trong cluster.

Ví dụ:

Attribute-based authorization là khi bạn liên kết một người dùng hoặc một nhóm người dùng với một tập hợp các quyền. Trong trường hợp này, chúng ta có thể nói rằng người dùng dev có thể xem, tạo và xóa pods. Bạn làm điều này bằng cách tạo một tệp policy với một tập hợp các chính sách được định nghĩa theo định dạng liền kề, và sau đó truyền tệp này vào API Server.

Tương tự, chúng ta tạo một tệp định nghĩa chính sách cho mỗi người dùng hoặc nhóm trong tệp này. Bây giờ, mỗi lần bạn cần thêm hoặc thay đổi cấu hình bảo mật, bạn phải chỉnh sửa tệp policy này thủ công và khởi động lại Kube API Server. Do đó, các cấu hình attribute-based access control khó quản lý hơn.

Role-based access control (RBAC) giúp việc này trở nên dễ dàng hơn nhiều. Với RBAC, thay vì liên kết trực tiếp người dùng hoặc nhóm với một tập hợp các quyền, chúng ta định nghĩa một role.

Trong trường hợp này là dành cho developer. Chúng ta tạo một role với tập hợp các quyền cần thiết cho các nhà phát triển, sau đó liên kết tất cả các nhà phát triển với role đó.

Tương tự, tạo một role cho người dùng bảo mật với bộ quyền cần thiết cho họ, sau đó liên kết người dùng với role đó.

Trong tương lai, mỗi khi cần thay đổi quyền truy cập của người dùng, chúng ta chỉ cần chỉnh sửa role và thay đổi đó sẽ ngay lập tức áp dụng cho tất cả các nhà phát triển. Role-based access control (RBAC) cung cấp một phương pháp chuẩn hóa hơn để quản lý quyền truy cập trong Kubernetes cluster.

Giả sử bạn muốn quản lý authorization bên ngoài và không thông qua các cơ chế tích hợp vừa rồi.

Ví dụ:

Open Policy Agent (OPA) là một công cụ bên thứ ba giúp với việc admission controlauthorization.

Bạn có thể cấu hình Kubernetes thực hiện một cuộc gọi API tới Open Policy Agent với thông tin về người dùng và yêu cầu quyền truy cập của họ, và để Open Policy Agent quyết định xem người dùng có được phép truy cập hay không. Dựa trên phản hồi đó, quyền truy cập sẽ được cấp cho người dùng.

Role Based Access Controls

💡
Làm thế nào để chúng ta tạo một role?

Chúng ta thực hiện điều đó bằng cách tạo một đối tượng role. Vì vậy, chúng ta tạo một tệp định nghĩa role:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
    name: developer
rules:
- apiGroups: [""]
  resources: ["apps"]
  verbs: ["list", "get", "update", "delete"]

Bước tiếp theo là liên kết người dùng với role đó. Để làm điều này, chúng ta tạo một đối tượng khác gọi là roleBinding. Đối tượng roleBinding liên kết một đối tượng người dùng với một role.

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
    name: devuser-developer-binding
subjects:
- kind: User
  name: dev-user
  apiGroup: rbac.authorization.k8s.io/v1
roleRef:
    kind: Role
    name: developer
    apiGroup: rbac.authorization.k8s.io/v1

Để xem các role đã được tạo, chạy lệnh:

$ kubectl get roles
NAME        AGE
developer    4s

Để xem các rolebinding, chạy lệnh:

$ kubectl get rolebindings
NAME                        AGE
devuser-developer-binding   4s

Nếu bạn, với tư cách là người dùng, muốn kiểm tra xem mình có quyền truy cập vào một tài nguyên cụ thể trong cluster hay không, bạn có thể sử dụng lệnh sau:

$ kubectl auth can-i <verb> <resource>/[rerourceName] -n <namespace>

Nếu bạn là quản trị viên, bạn thậm chí có thể mạo danh một người dùng khác để kiểm tra quyền của họ.

$ kubectl auth can-i <verb> <resource>/[resourceName] --as=<username> -n <namespace>

Cluster Roles and Role Bindings

Khi chúng ta nói về rolesrole bindings, chúng ta đã đề cập rằng chúng là có phạm vi namespace, nghĩa là chúng được tạo trong các namespace. Nếu bạn không chỉ định namespace, chúng sẽ được tạo trong namespace mặc định và chỉ kiểm soát quyền truy cập trong namespace đó.

💡
Bạn có thể nhóm hoặc cô lập các nodes trong một namespace không?

Không, những tài nguyên đó có phạm vi cluster-wide hoặc cluster-scoped. Chúng không thể được gán vào bất kỳ namespace cụ thể nào. Do đó, các tài nguyên được phân loại thành hai loại: namespaced hoặc cluster-scoped.

Bây giờ chúng ta đã thấy nhiều tài nguyên thuộc phạm vi namespace như pods, replica sets, jobs, deployments, services, secrets. Những tài nguyên này được tạo trong namespace mà bạn chỉ định khi tạo chúng. Nếu bạn không chỉ định namespace, chúng sẽ được tạo trong default namespace. Để xem, xóa hoặc cập nhật chúng, bạn luôn cần chỉ định namespace phù hợp.

Các tài nguyên có phạm vi cluster-scoped là những tài nguyên mà bạn không cần chỉ định namespace khi tạo, chẳng hạn như nodes, persistent volumes, hoặc các tài nguyên như cluster rolescluster role bindings.

Chúng ta thực hiện điều đó bằng cách tạo một đối tượng clusterRole. Vì vậy, chúng ta tạo một tệp định nghĩa clusterRole:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
    name: cluster-administrator
rules:
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["list", "get", "create", "delete"]

Bước tiếp theo là liên kết người dùng với role đó. Để làm điều này, chúng ta tạo một đối tượng khác gọi là clusterRoleBinding. Đối tượng clusterRoleBinding liên kết một đối tượng người dùng với một role.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
    name: cluster-admin-role-binding
subjects:
- kind: User
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io/v1
roleRef:
    kind: ClusterRole
    name: cluster-administrator
    apiGroup: rbac.authorization.k8s.io/v1

Network Policy

Chúng ta có một cụm với một tập hợp các node, lưu trữ một tập hợp các pod và service.

Mỗi node có 1 địa chỉ IP và mỗi pod cũng như service cũng có 1 địa chỉ IP như thế. Một trong những điều kiện tiên quyết để kết nối mạng trong Kubernetes, bất kể bạn triển khai giải pháp nào. Các pod có thể giao tiếp với nhau mà không cần phải cấu hình bất kỳ thiết lập bổ sung nào.

Ví dụ:

Trong giải pháp này, tất cả các pod nằm trong một mạng riêng ảo (VPN) bao phủ toàn bộ các node trong cụm Kubernetes và theo mặc định, chúng có thể kết nối với nhau bằng địa chỉ IP, tên pod, hoặc các dịch vụ được cấu hình cho mục đích đó.

Kubernetes được cấu hình mặc định với một quy tắc cho phép tất cả, cho phép lưu lượng truy cập từ bất kỳ pod nào đến bất kỳ pod hoặc dịch vụ nào khác trong cụm.

Đối với mỗi thành phần trong ứng dụng, chúng ta triển khai một pod. Một pod dành cho máy chủ web giao diện front-end, một pod cho máy chủ API, và một pod cho cơ sở dữ liệu.

Chúng ta tạo các dịch vụ để cho phép giao tiếp giữa các thành phần cũng như với người dùng cuối. Theo mặc định thì cả ba pod đều có thể giao tiếp với nhau trong cụm Kubernetes.

💡
Điều gì sẽ xảy ra nếu chúng ta không muốn máy chủ web giao diện front-end có thể giao tiếp trực tiếp với máy chủ cơ sở dữ liệu?

Giả sử: Các nhóm bảo mật và kiểm toán của bạn yêu cầu ngăn chặn điều đó xảy ra. Đây là lúc bạn sẽ triển khai một chính sách mạng (network policy) để chỉ cho phép lưu lượng truy cập đến máy chủ cơ sở dữ liệu từ máy chủ API.

Và chính sách mạng (network policy) là một đối tượng khác trong không gian tên (namespace) của Kubernetes, giống như các pod, replica sets hoặc dịch vụ. Bạn liên kết một chính sách mạng với một hoặc nhiều pod.

Bạn có thể định nghĩa các quy tắc trong chính sách mạng. Trong trường hợp này, tôi sẽ nói chỉ cho phép lưu lượng truy cập ingress từ pod API trên cổng 3306.

Khi chính sách này được tạo ra, nó sẽ chặn tất cả các lưu lượng truy cập khác đến pod và chỉ cho phép lưu lượng truy cập phù hợp với quy tắc đã chỉ định. Điều này chỉ áp dụng cho pod mà chính sách mạng được áp dụng.

💡
Vậy làm thế nào để áp dụng hoặc liên kết một chính sách mạng (network policy) với một pod?

Chúng ta gán nhãn (label) cho pod và sử dụng cùng một nhãn trong trường podSelector của chính sách mạng, sau đó xây dựng quy tắc của mình. Dưới loại chính sách (policy types), chỉ định xem quy tắc đó là để cho phép lưu lượng ingress (đến) hay egress (đi) hoặc cả hai.

Trong trường hợp của chúng ta, chúng ta chỉ muốn cho phép lưu lượng ingress (đến) vào pod cơ sở dữ liệu, vì vậy chúng ta thêm ingress. Tiếp theo, chúng ta chỉ định quy tắc ingress, cho phép lưu lượng từ pod API và chỉ định lại pod API bằng cách sử dụng nhãn (labels) và bộ chọn (selectors). Cuối cùng, chúng ta chỉ định cổng mà lưu lượng sẽ được cho phép, đó là cổng 3306.

policyTypes:
- Ingress
ingress:
- from:
    - podSelector:
        matchLabels:
            name: api-pod
    ports:
    - protocol: TCP
      port: 3306

Định nghĩa network policy:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
    name: db-policy
spec:
    podSelector:
        matchLabels:
            role: db
    policyTypes:
    - Ingress
    - Egress
    ingress:
    - from:
        - podSelector:
            matchLabels:
                name: api-pod
          namespaceSelector:
            matchLabels:
                name: prod
        # Cho truy cập vào pod từ ip cụ thể
        - ipBlock:
                cidr: 192.168.5.10/32
        ports:
        - protocol: TCP
          port: 3306
    egress:
    - to:
        - ipBlock:
                cidr: 192.168.5.10/32
        ports:
        - protocol: TCP
          port: 80
0
Subscribe to my newsletter

Read articles from Phan Văn Hoàng directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Phan Văn Hoàng
Phan Văn Hoàng