TLS Termination - Upstream TLS - Kubernetes kết hợp với MetalLB và Contour ingress controller trên Kubernetes On-Premises
Như bài viết trước nói về vấn đề kết hợp với MetalLB và Contour ingress controller trên Kubernetes On-Premises , có thể tham khảo bài trước đó để biết thêm chi tiết. Bài hôm nay là phần bổ trợ thêm đó là cài TLS cho dự án này để đảm bảo tính bảo mật hơn.
Trước tiên để việc với TLS thì cần cần hiểu một số khái niệm khái quát sau, khái quát thôi nhé muốn chuyên sâu về nó thì google thêm, vì nếu không hiểu khái niệm nó là gì thì chỉ thấy chạy lên là vỗ tay bốp bốp mà không hiểu thì cũng không hay lắm. Các khái niệm bên dưới sẽ giúp bạn những thứ bạn có thể đi tiếp, trong bài này chỉ là ví dụ cơ bản nó sẽ không diễn tả hết được, nhưng thông qua các keywork sẽ giúp mườn tượng ra được nhiều thứ hay ho để làm hơn.
TLS Termination
Là khái niệm chỉ việc kết thúc kết nối TLS ở một thiết bị trung gian thay vì ở điểm cuối.
Client (trình duyệt) thiết lập kết nối TLS tới Load Balancer.
Load Balancer giải mã dữ liệu TLS và chuyển tiếp dữ liệu đã giải mã tới các backend server (không còn mã hóa). Đây được gọi là TLS termination tại Load Balancer.
Client <===TLS===> Load balancer <===HTTP==> Server app
Mutual TLS
Cho phép cả server và client xác thực lẫn nhau trong quá trình thiết lập kết nối bảo mật.
Client sẽ xác thực danh tính của server bằng cách kiểm tra chứng chỉ (certificate) mà server cung cấp.
Server cũng sẽ xác thực danh tính của client bằng cách yêu cầu client cung cấp chứng chỉ.
Chỉ khi cả hai bên đều xác thực thành công danh tính của nhau, kết nối mới được thiết lập.
Client <===client & server auth ===> Load balancer <===client & server auth ==> Server app
Upstream TLS
Là việc sử dụng TLS ở phía upstream, tức là mã hóa kết nối giữa proxy server (ví dụ Nginx) và backend server (ví dụ API server). Proxy server sẽ mở kết nối TLS với backend server để đảm bảo dữ liệu truyền tải là riêng tư.
TLS Upstream
Là việc sử dụng TLS ở phía client, tức là mã hóa kết nối giữa client (trình duyệt web, ứng dụng...) và proxy server. Client sẽ mở kết nối TLS với proxy server để đảm bảo dữ liệu truyền tải từ client lên là riêng tư.
Client <===TLS===> Load balancer <===TLS ==> Server app
Cấu hình môi trường thì dùng lại lab bài hôm trước, click vào link trên để xem, giờ có các manifest và kịch bạn theo lý thuyết ở trên sẽ như sau :
TLS Termination :
Đối với phần này thì khá đơn giản, copy lại manifest của bài trước, thêm vài thứ vào là xong, phần này khá là nhẹ.
Tạo namespace
kind: Namespace apiVersion: v1 metadata: name: chemgio-project-tls
Tạo cert và tạo secret
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt
~/chemgio-project-tls/certs$ k create secret tls chemgio-secret-project-tls --key=ca.key --cert=ca.crt -n chemgio-project-tls
Tạo deploy
kind: Deployment apiVersion: apps/v1 metadata: name: chemgio-project-tls namespace: chemgio-project-tls spec: replicas: 2 selector: matchLabels: app: chemgio-project-tls template: metadata: labels: app: chemgio-project-tls spec: containers: - image: nginx imagePullPolicy: IfNotPresent name: http ports: - containerPort: 80 protocol: TCP resources: {}
Tạo service
kind: Service
apiVersion: v1
metadata:
name: chemgio-project-tls
namespace: chemgio-project-tls
spec:
selector:
app: chemgio-project-tls
ports:
- port: 80
targetPort: 80
Tạo HTTPProxy
apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
name: chemgio-project-tls
namespace: chemgio-project-tls
spec:
virtualhost:
fqdn: chemgio.loc
tls:
secretName: chemgio-secret-project-tls
routes:
- services:
- name: chemgio-project-tls
port: 80
Cuối cùng là xong phần TLS Termination, phần này khá nhẹ nhàng và đơn giản, vì bên trong không bận tâm gì nhiều. :)
Upstream TLS :
Tạo Namespace
kind: Namespace
apiVersion: v1
metadata:
name: demo1-project-tls-upstream
Tạo ConfigMap : Có thể tạo config map từ manifest or import từ default.conf cũng được.
apiVersion: v1
data:
default.conf: |-
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
server_name localhost;
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name localhost;
ssl_certificate /etc/nginx/ssl/tls.crt;
ssl_certificate_key /etc/nginx/ssl/tls.key;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
kind: ConfigMap
metadata:
name: demo1-configmap-tls-upstream
namespace: demo1-project-tls-upstream
Tạo Secrets : ở đây là môi trường lab, nhằm phục vụ cho môi trường private nên hiện tại đang dùng openssl để giả lập cert, với môi trường thực tế thì thay đổi lại sao cho phù hợp.
openssl genpkey -algorithm RSA -out private.key
openssl rsa -pubout -in private.key -out public.pem
openssl req -new -key private.key -out server.csr
openssl genpkey -algorithm RSA -out ca-private.key
openssl req -new -key ca-private.key -out ca-csr.pem
openssl x509 -req -in ca-csr.pem -signkey ca-private.key -out ca-certificate.pem
openssl x509 -req -in server.csr -CA ca-certificate.pem -CAkey ca-private.key -CAcreateserial -out server-certificate.pem
Sau khi chạy các lệnh trên thì được danh sách file như sau:
ca-certificate.pem ca-csr.pem ca-private.key private.key public.pem server-certificate.pem server.csr
Sử dụng các file cần thiết như bên dưới để tạo secret, theo dõi bên dưới để biết file nào cần dùng.
Tạo secret private
kubectl create secret tls demo1-secret-tls-upstream-private --key="private.key" --cert="server-certificate.pem" -n demo1-project-tls-upstream
kubectl create secret tls demo1-secret-tls-upstream-public --key="ca-private.key" --cert="ca-certificate.pem" -n demo1-project-tls-upstream
kind: Deployment
apiVersion: apps/v1
metadata:
name: demo1-project-tls-upstream
namespace: demo1-project-tls-upstream
spec:
replicas: 2
selector:
matchLabels:
app: demo1-project-tls-upstream
template:
metadata:
labels:
app: demo1-project-tls-upstream
spec:
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: http
ports:
- containerPort: 80
- containerPort: 443
volumeMounts:
- name: demo1-secret-tls-upstream-private
mountPath: /etc/nginx/ssl
- name: demo1-project-tls-upstream
mountPath: /etc/nginx/conf.d
volumes:
- name: demo1-secret-tls-upstream-private
secret:
secretName: demo1-secret-tls-upstream-private
- name: demo1-project-tls-upstream
configMap:
name: demo1-configmap-tls-upstream
apiVersion: v1
kind: Service
metadata:
name: demo1-project-tls-upstream
namespace: demo1-project-tls-upstream
annotations:
projectcontour.io/upstream-protocol.tls: "443,https"
spec:
ports:
- name: https
port: 443
targetPort: 443
selector:
app: demo1-project-tls-upstream
apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
name: demo1-project-tls-upstream
namespace: demo1-project-tls-upstream
spec:
virtualhost:
fqdn: demo1.loc
tls:
secretName: demo1-secret-tls-upstream-public
routes:
- services:
- name: demo1-project-tls-upstream
port: 443