- Đăng vào
Tạo chứng chỉ Let's Encrypt SSL cho Gateway API trên Kubernetes
Giới thiệu
Ở bài viết: Sử dụng Gateway API trên Kubernetes, mình đã giới thiệu các cấu hình Gateway thông qua HTTPRoute để truy cập vào ứng dụng được triển khai trên Kubernetes. Tuy nhiên ứng được truy cập thông qua giao thức HTTP có độ bảo mật kém, ở bài viết này mình sẽ hướng dẫn các bước để chúng ta có thể tạo chứng chi SSL cấp bởi Let's Encrypt cho Gateway. Từ đó cho phép truy cập vào ứng dụng thông qua giao thức HTTPS, và điều hướng mọi truy cập HTTP sang thành HTTPS.
Cấu hình Cert-manager.io
cert-manager là một controller mạnh mẽ và có khả năng mở rộng cho quản lý chứng chỉ X.509 trên các khối công việc Kubernetes và OpenShift. Nó sẽ thu thập chứng chỉ từ nhiều Issuer khác nhau, bao gồm cả Issuer công cộng phổ biến và Issuer riêng tư, đồng thời đảm bảo rằng các chứng chỉ này hợp lệ và cập nhật. Nó cũng sẽ tự động thử gia hạn chứng chỉ trước khi chúng hết hạn123.
Một số điểm nổi bật của cert-manager:
- Tự động cấp phát và gia hạn chứng chỉ để bảo vệ Ingress với TLS.
- Hỗ trợ Issuer từ các Cơ quan chứng chỉ công nhận cũng như PKI riêng tư.
- Bảo mật giao tiếp pod-to-pod với mTLS sử dụng PKI riêng tư.
- Hỗ trợ các trường hợp sử dụng chứng chỉ cho các khối công việc web-facing và internal.
- Miễn phí và mã nguồn mở.
Đầu tiên chúng ta phải cài cert-manager.io trên Kubernetes cluster:
- Tải về manifest
curl -LJO https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml
- Mặc định thì cert-manager chưa tích hợp sẵn với Gateway, chúng ta phải sửa file manifest vừa download về bằng cách thêm vào cert-manager Deployment:
- --feature-gates=ExperimentalGatewayAPISupport=true
cert-manager.yaml... containers: - name: cert-manager-controller image: "quay.io/jetstack/cert-manager-controller:v1.14.4" imagePullPolicy: IfNotPresent args: - --v=2 - --cluster-resource-namespace=$(POD_NAMESPACE) - --leader-election-namespace=kube-system - --acme-http01-solver-image=quay.io/jetstack/cert-manager-acmesolver:v1.14.4 - --max-concurrent-challenges=60 - --feature-gates=ExperimentalGatewayAPISupport=true ports: - containerPort: 9402 name: http-metrics protocol: TCP ...
- Bây giờ chúng cài đặt nó trên cluster:
kubectl apply -f cert-manager.yaml
Thiết lập Cluster issuer
Dưới đây mình sẽ giới thiệu cách tạo Let's Encrypt certificate cho một Gateway với thông tin. (Gateway này đã được triển khai theo bài viết trong phần giới thiệu)
- Tên gatewway:
istio-gateway
- Namespace của gateway:
istio-ingress
Để biết cách tạo Gateway, vui lòng xem trong bài viết được đề cập ở phần giới thiệu
# Dev ClusterIssue
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-dev
spec:
selfSigned: {}
---
# Staging ClusterIssue
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
# The ACME server URL
server: https://acme-staging-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: you@email.com
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-staging
# Enable the HTTP-01 challenge provider
solvers:
- http01:
gatewayHTTPRoute:
parentRefs:
- name: istio-gateway
namespace: istio-ingress
kind: Gateway
# Production ClusterIssue
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# The ACME server URL
server: https://acme-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: you@email.com
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-prod
# Enable the HTTP-01 challenge provider
solvers:
- http01:
gatewayHTTPRoute:
parentRefs:
- name: istio-gateway
namespace: istio-ingress
kind: Gateway
Bây giờ chúng ta chỉ cần triển khai lên Kubernetes:
kubectl apply -f certificate-issuers.yaml
Thông thường chúng ta sẽ dùng cluster issuer letsencrypt-prod
, tuy nhiên để tránh tình trạng rate limit của Let's Encrypt, chúng ta nên dùng letsencrypt-dev
hoặc letsencrypt-staging
để test, khi mọi thứ ok chúng ta chuyển qua dùng letsencrypt-prod
.
Cấu hình Gateway
Bước tiếp theo chúng ta sẽ cập nhật manifest của Gateway istio-gateway
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: istio-gateway
namespace: istio-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
gatewayClassName: istio
listeners:
- name: http
protocol: HTTP
port: 80
hostname: "httpbin.yourdomain.com"
allowedRoutes:
namespaces:
from: All
- name: httpbin
hostname: httpbin.yourdomain.com
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: ssl-httpbin.yourdomain.com # Tên của certificate sẽ được lưu
allowedRoutes:
namespaces:
from: All
Những thay đổi trên Gateway mình đã highlight, chúng ta giờ sẽ apply lại manifest
kubectl apply -f istio-gateway.yaml
Giờ là bước cuối cùng, chúng ta cập nhật lại HTTPRoute để điều hướng request sang giao thức HTTPS
Cập nhật HTTPRoute
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: httpbin
namespace: test-istio
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: istio-gateway
namespace: istio-ingress
sectionName: httpbin
hostnames: ["httpbin.yourdomain.com"]
rules:
- matches:
- path:
type: PathPrefix
value: /headers
backendRefs:
- name: httpbin
port: 8000
group: ''
kind: Service
weight: 1
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: tls-redirect
namespace: test-istio
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: istio-gateway
namespace: istio-ingress
sectionName: http
hostnames:
- httpbin.yourdomain.com
rules:
- filters:
- type: RequestRedirect
requestRedirect:
scheme: https
port: 443
statusCode: 302
matches:
- path:
type: PathPrefix
value: /headers
Tóm tắt một số thay đổi mình đã highlight ở trên
- Line 13: trở đến listener
httpbin
port 443 trên Gateway - Thêm HTTPRoute dùng để redirect từ http sang https
Chúng ta sẽ apply lại httproute mainifest:
kubectl apply -f httproute.yaml
Chúng ta kiểm tra xem mọi thứ đã hoạt động OK hay chưa. Thử request thông qua HTTP
curl -v http://httpbin.yourdomain.com/headers
* Trying 11.12.69.53:80...
* Connected to httpbin.yourdomain.com (11.12.69.53) port 80 (#0)
> GET /headers HTTP/1.1
> Host: httpbin.yourdomain.com
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 Found
< location: https://httpbin.yourdomain.com/headers
< date: Sun, 17 Mar 2024 06:08:31 GMT
< server: istio-envoy
< content-length: 0
<
* Connection #0 to host httpbin.nvtienanh.info left intact
Kiết quả lè nhận được code 302 (Redirect sang https://httpbin.yourdomain.com/headers)
Bây giờ chúng ta thử request thông qua HTTPS:
curl https://httpbin.yourdomain.com/headers
{
"headers": {
"Accept": "*/*",
"Host": "httpbin.yourdomain.com",
"User-Agent": "curl/7.81.0",
"X-B3-Sampled": "0",
"X-B3-Spanid": "7b517be14b",
"X-B3-Traceid": "c2b167b8ef72b9a40b517be14b",
"X-Envoy-Attempt-Count": "1",
"X-Envoy-Decorator-Operation": "httpbin.test-istio.svc.cluster.local:8000/*",
"X-Envoy-Internal": "true",
"X-Envoy-Peer-Metadata": "ChQKDkFQUF9DT05UQUlORVJTEgIaAA---aW8tZ2F0ZXdheS1pc3Rpbw==",
"X-Envoy-Peer-Metadata-Id": "router~10.244.133.198~istio-gateway-istio-6cb5879dd6-wwtvw.istio-ingress~istio-ingress.svc.cluster.local"
}
}
Như vậy mọi thứ đã hoạt động đúng như mong đợi.
Chúc thành công,
ANH NGUYỄN