Thứ Sáu, 28 tháng 8, 2026

Nginx mTLS

 


A. Nginx as SERVER with mTLS
Trong  hệ thống nội bộ, ca certificate thường dùng chung cả cho server và client.
Nhưng nếu bảo mật hơn, có thể tách riêng ra. Xem phần C.
Ở phần A này tôi sẽ dùng chung
1.1 Generate the CA Certificate and Key
openssl genpkey -algorithm RSA -out /etc/nginx/cert/ca.key
openssl req -days 3650 -new -x509 -key /etc/nginx/cert/ca.key -out /etc/nginx/cert/ca.crt -subj "/OU=CA-OrgUnit/CN=CA-MASTER"
openssl x509 -text -noout -in /etc/nginx/cert/ca.crt
1.2 Generate the Server Certificate and Key
openssl genpkey -algorithm RSA -out /etc/nginx/cert/server.key
openssl req -new -key /etc/nginx/cert/server.key -out /etc/nginx/cert/server.csr -subj "/OU=MyOrgUnit/CN=myserver.com.vn"
openssl x509 -req -days 3650 -in /etc/nginx/cert/server.csr -CA /etc/nginx/cert/ca.crt -CAkey /etc/nginx/cert/ca.key -CAcreateserial -out /etc/nginx/cert/server.crt
openssl x509 -text -noout -in /etc/nginx/cert/server.crt
1.3 Generate the Client Certificate and Key
openssl genpkey -algorithm RSA -out /etc/nginx/cert/client.key
openssl req -new -key /etc/nginx/cert/client.key -out /etc/nginx/cert/client.csr -subj "/OU=MyOrgUnit/CN=MyClient"
openssl x509 -req -days 3650 -in /etc/nginx/cert/client.csr -CA /etc/nginx/cert/ca.crt -CAkey /etc/nginx/cert/ca.key -CAcreateserial -out /etc/nginx/cert/client.crt
openssl x509 -text -noout -in /etc/nginx/cert/client.crt
2 Configure Nginx for SSL/TLS and mTLS
 server {
 listen 443 ssl;
    # Server certificate and key
     ssl_certificate /etc/nginx/cert/server.crt;
     ssl_certificate_key /etc/nginx/cert/server.key;

    # CA certificate for client verification
     ssl_client_certificate /etc/nginx/cert/ca.crt;
     ssl_verify_client optional;

          location / {
                  default_type text/plain;
                  if ($ssl_client_verify != SUCCESS) {
                      return 403 'blocked access to mTLS-protected resource';
                  }
                  return 200 'access to mTLS-protected resource';
          }

}
Reload nginx apply new config
nginx -s reload
3: Testing mTLS with cURL
curl -vk https://localhost
> blocked access to mTLS-protected resource
curl --cert /etc/nginx/cert/client.crt --key /etc/nginx/cert/client.key --cacert /etc/nginx/cert/ca.crt https://localhost
> access to mTLS-protected resource


openssl s_client -connect localhost:443 -cert /etc/nginx/cert/client.crt -key /etc/nginx/cert/client.key -CAfile /etc/nginx/cert/ca.crt
________________________________________
Refer: https:/medium.com/@mahernaija/new-2025-how-to-configure-mutual-tls-mtls-for-secure-nginx-206f983ba571


B. Nginx as CLIENT in mTLS
Chuẩn bị bộ client cert/key. Ngoài ra upstream_ca.crt chính là Ca-Certificate

server {
    listen 80;
    server_name my-gateway.local;

    location / {
        # 1. Đường dẫn tới Server backend yêu cầu mTLS
        proxy_pass https://secure-upstream-server.local:443;

        # 2. Bật các cấu hình SSL cho Proxy
        proxy_ssl_server_name on; # Gửi SNI (Server Name Indication) nếu cần
        # Định nghĩa tên miền sẽ gửi trong TLS Handshake đến Upstream
        # Thường sử dụng biến $host (tên miền client gửi lên) hoặc một chuỗi cố định như 'backend.com'
        proxy_ssl_name $host;

        # 3. Gửi chứng chỉ và khóa của Nginx (đóng vai trò Client) lên Server
        proxy_ssl_certificate     /etc/nginx/certs/client.crt;
        proxy_ssl_certificate_key /etc/nginx/certs/client.key;

        # 4. Xác thực chứng chỉ của Server backend để đảm bảo an toàn
        proxy_ssl_trusted_certificate /etc/nginx/certs/upstream_backend_ca.crt;
        proxy_ssl_verify             on;
        proxy_ssl_verify_depth       2;

        # Các cấu hình proxy thông thường khác
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

C. Chia riêng rẽ ca certificate client/server
Không muốn dùng chung. Ta sẽ làm như sau
1. Tạo "Upstream CA" và chứng chỉ cho Upstream Server:*8443
mkdir -p /etc/nginx/certs && cd /etc/nginx/certs

# 1.1 Tạo Upstream CA (Dùng để ký cho Server)
openssl genrsa -out upstream_ca.key 4096
openssl req -new -x509 -days 3650 -key upstream_ca.key -out upstream_ca.crt -subj "/CN=UpstreamServerRootCA"

# 1.2 Tạo chứng chỉ cho Upstream Server (Sẽ cài trên máy chủ backend)
openssl genrsa -out upstream_server.key 2048
openssl req -new -key upstream_server.key -out upstream_server.csr -subj "/CN=secure-upstream.local"
openssl x509 -req -days 365 -in upstream_server.csr -CA upstream_ca.crt -CAkey upstream_ca.key -CAcreateserial -out upstream_server.crt

2. Tạo "Client CA" và chứng chỉ cho Nginx Client:80
mkdir -p /etc/nginx/certs && cd /etc/nginx/certs

# 2.1 Tạo Client CA riêng biệt (Dùng để ký cho các Client/Nginx)
openssl genrsa -out client_ca.key 4096
openssl req -new -x509 -days 3650 -key client_ca.key -out client_ca.crt -subj "/CN=MyClientInternalCA"

# 2.2 Tạo chứng chỉ cho Nginx Client (Sẽ cài trên Nginx Proxy)
openssl genrsa -out nginx_client.key 2048
openssl req -new -key nginx_client.key -out nginx_client.csr -subj "/CN=NginxProxyClient"
openssl x509 -req -days 365 -in nginx_client.csr -CA client_ca.crt -CAkey client_ca.key -CAcreateserial -out nginx_client.crt

Cấu hình nginx
1.  Khối Server 1 (Nginx Client / Reverse Proxy): Nhận HTTP cổng 80 từ người dùng → Dùng mTLS truyền tiếp lên Backend.
2.  Khối Server 2 (Upstream Server / Backend): Chạy HTTPS cổng 8443 → Yêu cầu mTLS và chỉ tin tưởng chứng chỉ từ Client CA.
Sơ đồ: client  nginx:80 + Proxy pass Backend :8443 with mtls - Nginx:8443 with mtls
# ====================================================================
# VAI TRÒ 1: NGINX ĐÓNG VAI TRÒ CLIENT (REVERSE PROXY)
# ====================================================================
server {
    listen 80;
    server_name localhost;

    location / {
        # Chuyển tiếp tới Upstream Server chạy HTTPS cổng 8443
        proxy_pass https://secure-upstream.local:8443;
        proxy_ssl_server_name on; #send SNI

        # ------------------------------------------------------------
        # [A] XÁC THỰC SERVER: Nginx kiểm tra danh tính của Upstream Server
        # ------------------------------------------------------------
        proxy_ssl_verify             on;
        # CHỈ tin tưởng Server nếu chứng chỉ của nó do "Upstream CA" ký
        proxy_ssl_trusted_certificate /etc/nginx/certs/upstream_ca.crt;
        proxy_ssl_verify_depth       4;

        # ------------------------------------------------------------
        # [B] XÁC THỰC CLIENT: Nginx gửi chứng chỉ của mình lên Server
        # ------------------------------------------------------------
        # Chứng chỉ này được tạo và ký bởi "Client CA" riêng biệt, sẽ gửi sang server để verify
        proxy_ssl_certificate         /etc/nginx/certs/nginx_client.crt;
        proxy_ssl_certificate_key     /etc/nginx/certs/nginx_client.key;

        # Các cấu hình header cơ bản
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

# ====================================================================
# VAI TRÒ 2: UPSTREAM SERVER (GIẢ LẬP MÁY CHỦ BACKEND NHẬN KẾT NỐI mTLS)
# ====================================================================
server {
    listen 8443 ssl;
    server_name secure-upstream.local;

    # Chứng chỉ của bản thân Server (Được ký bởi Upstream CA). Như 1 ssl bình thường nhưng phơi ra để client kiểm tra.
    ssl_certificate         /etc/nginx/certs/upstream_server.crt;
    ssl_certificate_key     /etc/nginx/certs/upstream_server.key;

    # Cấu hình mTLS phía nhận: Bắt buộc phải có chứng chỉ Client hợp lệ
    ssl_verify_client       on;
    # CHỈ chấp nhận chứng chỉ Client nếu nó được ký bởi "Client CA"
    ssl_client_certificate  /etc/nginx/certs/client_ca.crt;
    ssl_verify_depth        2;

    location / {
        # Trả về phản hồi text nếu mTLS thành công tốt đẹp
        add_header Content-Type text/plain;
        return 200 "Chúc mừng! Kết nối mTLS giữa Nginx Client và Upstream Server thành công bằng 2 CA độc lập.\n";
    }
}

Khi gọi trực tiếp vào 8443 sẽ bị lỗi
Đứng ở client ta phải cung cấp đủ 3 key mới gọi được
# curl --cacert /etc/nginx/certs/upstream_ca.crt --key /etc/nginx/certs/client_ca.key --cert /etc/nginx/certs/client_ca.crt https://secure-upstream.local:8443
Chúc mừng! Kết nối mTLS giữa Nginx Client và Upstream Server thành công bằng 2 CA độc lập.

# curl localhost:80
Chúc mừng! Kết nối mTLS giữa Nginx Client và Upstream Server thành công bằng 2 CA độc lập.



Thứ Năm, 6 tháng 8, 2026

Nginx SNI

Nginx SNI
SNI chia làm 2 loại SNI server SNI Client

1. Nginx reject non-SNI server side
# Khối server mặc định để chặn mọi truy cập không khớp SNI,
yêu cầu phải gửi servername sni lên cùng
server {
    listen 443 default_server ssl;
    listen [::]:443 default_server ssl;

    # Từ chối bắt tay SSL ngay lập tức
    ssl_reject_handshake on;
}

# Cấu hình website hợp lệ của bạn
server {
    listen 443 ssl;
    server_name yourdomain.com;

    ssl_certificate     /path/to/cert.crt;
    ssl_certificate_key /path/to/cert.key;
    # ... cấu hình khác
}


2. SNI as client  side (forward proxy)
Mặc định, Nginx không tự động gửi SNI khi kết nối với Upstream.
Nếu máy chủ Upstream yêu cầu SNI để trả về chứng chỉ đúng,
bạn bắt buộc phải bật cấu hình Client-side SNI thông qua các
chỉ thị proxy_ssl_server_name proxy_ssl_name
server {
    listen 443 ssl;
    server_name proxy.example.com;

    ssl_certificate /etc/ssl/certs/proxy.crt;
    ssl_certificate_key /etc/ssl/certs/proxy.key;

    location / {
        # 1. Chuyển tiếp request đến upstream qua HTTPS
        proxy_pass https://backend_server;

        # 2. BẬT Client-side SNI (Mặc định là off)
        proxy_ssl_server_name on;

        # 3. Định nghĩa tên miền sẽ gửi trong TLS Handshake đến Upstream
        # Thường sử dụng biến $host (tên miền client gửi lên) hoặc một chuỗi cố định như 'backend.com'
        proxy_ssl_name $host;

        # (Tùy chọn) Đảm bảo Header Host của HTTP cũng trùng khớp với SNI
        proxy_set_header Host $host;
    }
}

TLS Handshake Flow with SNI
 




Thứ Tư, 29 tháng 4, 2026

GatewayAPI collection

MTLS



# ===Bước 1: Tạo Backend CA & Chứng chỉ cho Ứng dụng=================

# 1. Tạo Backend Root CA độc lập

openssl genrsa -out backend_ca.key 4096

openssl req -x509 -new -nodes -key backend_ca.key -sha256 -days 36500 \

  -out backend_ca.crt \

  -subj "/CN=BackendRootCA"


# 2. Tạo chứng chỉ Server cho secure-app

openssl genrsa -out secure_app.key 2048

openssl req -new -key secure_app.key \

  -out secure_app.csr \

  -subj "/C=VN/ST=HaNoi/O=My Company/CN=test.tuan.name.vn"


# 3. Ký chứng chỉ Server bằng Backend Root CA

openssl x509 -req -in secure_app.csr \

  -CA backend_ca.crt -CAkey backend_ca.key \

  -CAcreateserial -out secure_app.crt \

  -days 365 -sha256


# ===Bước 2: Tạo Gateway CA & Chứng chỉ Client cho Gateway=================

# 1. Tạo Gateway Root CA độc lập

openssl genrsa -out gateway_ca.key 4096

openssl req -x509 -new -nodes -key gateway_ca.key -sha256 -days 36500 \

  -out gateway_ca.crt \

  -subj "/CN=GatewayRootCA"


# 2. Tạo chứng chỉ Client cho Gateway

openssl genrsa -out gateway_client.key 2048

openssl req -new -key gateway_client.key \

  -out gateway_client.csr \

  -subj "/CN=gateway"


# 3. Ký chứng chỉ Client bằng Gateway Root CA

openssl x509 -req -in gateway_client.csr \

  -CA gateway_ca.crt -CAkey gateway_ca.key \

  -CAcreateserial -out gateway_client.crt \

  -days 365 -sha256




# 1. app-tls-secret: 

# Tạo backend secret phía cuối: ( bao gồm cert ,key của backend và gateway_ca.crt của gateway)

kubectl -n infra-gateway26 create secret generic app-tls-secret --from-file=tls.crt=secure_app.crt --from-file=tls.key=secure_app.key --from-file=ca.crt=gateway_ca.crt --dry-run=client -o yaml | kubectl apply -f -


# 2. gateway-presents-this-cert-for-validation

# Chứng chỉ Client của Gateway, dùng để "trình diện" cho Backend (ca.crt) kiểm tra nhằm chứng minh mình là Gateway hợp pháp.

kubectl -n infra-gateway26 create secret tls gateway-presents-this-cert-for-validation --cert=gateway_client.crt --key=gateway_client.key --dry-run=client -o yaml | kubectl apply -f -


# 3. backend-cert #Chú ý  backend-cert để cho vào BackendTLSPolicy phải là configmap, không được dùng như secret

# Vị trí nằm ở: Được khai báo trong BackendTLSPolicy của Kubernetes để NGINX Gateway Fabric sử dụng.

# File chứng chỉ của Root CA (backend_ca.crt) của cụm backend.

kubectl -n infra-gateway26 create configmap backend-cert --from-file=ca.crt=backend_ca.crt --dry-run=client -o yaml | kubectl apply -f -




Tạo deployment

apiVersion: apps/v1

kind: Deployment

metadata:

  name: secure-app

  namespace: infra-gateway26

spec:

  replicas: 1

  selector:

    matchLabels:

      app: secure-app

  template:

    metadata:

      labels:

        app: secure-app

    spec:

      containers:

        - name: secure-app

          image: nginxinc/nginx-unprivileged:latest

          ports:

            - containerPort: 8443

          volumeMounts:

            - name: secret

              mountPath: /etc/nginx/ssl/secret

              readOnly: true

            - name: config-volume

              mountPath: /etc/nginx/conf.d

            - name: nginx-config

              mountPath: /etc/nginx/

      volumes:

        - name: secret

          secret:

            secretName: app-tls-secret

        - name: config-volume

          configMap:

            name: secure-config

        - name: nginx-config

          configMap:

            name: nginx-config

---

apiVersion: v1

kind: Service

metadata:

  name: secure-app

  namespace: infra-gateway26

spec:

  ports:

    - port: 8443

      targetPort: 8443

      protocol: TCP

      name: https

  selector:

    app: secure-app

---

apiVersion: v1

kind: ConfigMap

metadata:

  name: secure-config

  namespace: infra-gateway26

data:

  app.conf: |-

    server {

        listen 8443 ssl;

        listen [::]:8443 ssl;


        server_name secure-app.example.com;


        default_type text/plain;


        ssl_certificate /etc/nginx/ssl/secret/tls.crt;

        ssl_certificate_key /etc/nginx/ssl/secret/tls.key;


        ssl_client_certificate /etc/nginx/ssl/secret/ca.crt;

        ssl_verify_client on;


        # Enable access logging

        access_log /var/log/nginx/access.log ssl_log;


        location / {

            return 200 "hello from pod secure-app\n";

        }

    }

---

apiVersion: v1

kind: ConfigMap

metadata:

  name: nginx-config

  namespace: infra-gateway26

data:

  nginx.conf: |

    worker_processes auto;

    pid /tmp/nginx.pid;


    events {

        worker_connections 1024;

    }


    http {

        log_format ssl_log '$remote_addr ssl_client_verify=$ssl_client_verify ssl_client_subject=$ssl_client_s_dn';

        error_log /var/log/nginx/error.log debug;

        include /etc/nginx/conf.d/*.conf;

    }


Tạo GW

apiVersion: gateway.networking.k8s.io/v1

kind: Gateway

metadata:

  name: gateway

  namespace: infra-gateway26

spec:

  gatewayClassName: nginx

  listeners:

  - name: http

    port: 80

    protocol: HTTP


Tạo HTTPRoute

apiVersion: gateway.networking.k8s.io/v1

kind: HTTPRoute

metadata:

  name: secure-app

  namespace: infra-gateway26

spec:

  parentRefs:

  - name: gateway

    sectionName: http

  hostnames:

  - "test.tuan.name.vn"

  rules:

  - matches:

    - path:

        type: PathPrefix

        value: /

    backendRefs:

    - name: secure-app

      port: 8443


Khi đó gọi sẽ lỗi vì chưa có BackEndTLSPolicies

# curl --resolve test.tuan.name.vn:80:192.168.88.150 http://test.tuan.name.vn

<html>

<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>

<body>


Sửa lại gw tại 2 điểm:

  • Gw phải đưa vào --cert=gateway_client.crt --key=gateway_client.key. Giúp cho backend verify đúng là gw client.

  • GW phải đưa vào backend_ca.crt để kiểm tra backend destination có đúng không.


apiVersion: gateway.networking.k8s.io/v1

kind: Gateway

metadata:

  name: gateway

  namespace: infra-gateway26

spec:

  gatewayClassName: nginx

  listeners:

  - name: http

    port: 80

    protocol: HTTP

  tls:

    backend:

      clientCertificateRef:

        name: gateway-presents-this-cert-for-validation #tương đương proxy_ssl_certificate 

        kind: Secret


apiVersion: gateway.networking.k8s.io/v1

kind: BackendTLSPolicy

metadata:

  name: backend-tls

  namespace: infra-gateway26

spec:

  targetRefs:

  - group: ""

    kind: Service

    name: secure-app

  validation:

    hostname: "test.tuan.name.vn"

    caCertificateRefs:

    - name: backend-cert

      group: ''

      kind: ConfigMap #Chú ý đây là configmap, không dùng secret


Sau đó thực hiện test lại

curl --resolve test.tuan.name.vn:80:192.168.88.150 http://test.tuan.name.vn

hello from pod secure-app





ESTED-21: BackendTLSPolicy

Việc dùng BackendTLSPolicy là cho trường  hợp backend đằng sau có SSL

https://gateway-api.sigs.k8s.io/api-types/backendtlspolicy/#:~:text=are%20not%20allowed.-,WellKnownCACertificates,of%20choice%20for%20more%20information


https://gateway-api.sigs.k8s.io/guides/tls/#using-system-certificates 


k create ns app-namespace21

cat << EOF > 21.configmap.yaml

apiVersion: v1

kind: ConfigMap

metadata:

  name: nginx-config

  namespace: app-namespace21

data:

  nginx.conf: |

    server {

        listen 80;

        server_name localhost;

        

        # Tự động chuyển hướng từ HTTP sang HTTPS (Tùy chọn)

        return 301 https://$host$request_uri;

    }


    server {

        listen 443 ssl;

        server_name test.tuan.name.vn;


        ssl_certificate /etc/nginx/ssl/tls.crt;

        ssl_certificate_key /etc/nginx/ssl/tls.key;


        location / {

            root /usr/share/nginx/html;

            index index.html index.htm;

        }

    }

EOF


k apply -f 21.configmap.yaml


openssl req -x509 -nodes -days 365 -newkey rsa:2048   -keyout tls.key   -out tls.crt   -subj "/CN=*.tuan.name.vn/O=Default Certificate"

kubectl -n app-namespace21 create secret tls nginx-ssl-secret --key tls.key   --cert tls.crt 


cat << EOF > 21.deployment.yaml

apiVersion: apps/v1

kind: Deployment

metadata:

  name: nginx-deployment

  namespace: app-namespace21

  labels:

    app: nginx

spec:

  replicas: 1

  selector:

    matchLabels:

      app: nginx

  template:

    metadata:

      labels:

        app: nginx

    spec:

      containers:

      - name: nginx

        image: nginx:latest

        ports:

        - containerPort: 80

          name: http

        - containerPort: 443

          name: https

        volumeMounts:

        # Gắn file cấu hình NGINX vào container

        - name: config-volume

          mountPath: /etc/nginx/conf.d/default.conf

          subPath: nginx.conf

        # Gắn chứng chỉ SSL từ Secret vào container

        - name: ssl-volume

          mountPath: /etc/nginx/ssl

          readOnly: true

      volumes:

      - name: config-volume

        configMap:

          name: nginx-config

      - name: ssl-volume

        secret:

          secretName: nginx-ssl-secret

EOF


k apply -f 21.deployment.yaml


kubectl -n app-namespace21 expose deployment nginx-deployment --name=nginx-service-http --port=80 --target-port=80 --type=ClusterIP

kubectl -n app-namespace21 expose deployment nginx-deployment --name=nginx-service-https --port=443 --target-port=443 --type=ClusterIP




apiVersion: gateway.networking.k8s.io/v1

kind: Gateway

metadata:

  name: external-gateway

  namespace: infra-gateway21

spec:

  gatewayClassName: nginx

  listeners:

  - name: http-listener

    protocol: HTTP

    port: 80

    allowedRoutes:

      namespaces:

        from: All

---

apiVersion: gateway.networking.k8s.io/v1

kind: HTTPRoute

metadata:

  name: nginx-http-route

  namespace: app-namespace21

spec:

  parentRefs:

  - name: external-gateway

    namespace: infra-gateway21

    sectionName: http-listener

  hostnames:

  - "test.tuan.name.vn"

  rules:

  - backendRefs:

    - name: nginx-service-https

      port: 443


Lựa chọn 1 trong 2 cách:

CÁCH 1: 

# Chú ý cert của deployment nên là cert xịn, nếu cert đểu thì gọi lỗi 502 bad gateway. Vì khi setting là System thì pod sẽ lấy chính CA được public để so sánh cert của backend

apiVersion: gateway.networking.k8s.io/v1

kind: BackendTLSPolicy

metadata:

  name: gateway-to-backend-tls

  namespace: app-namespace21

spec:

  targetRefs:

  - group: ""

    kind: Service

    name: nginx-service-https

  validation:

    hostname: "test.tuan.name.vn"

    wellKnownCACertificates: System


CÁCH 2: Nếu cert là đểu, thì dùng cách này

# CHÚ Ý: BACKEND TLS không được tạo từ Secret, Mà phải được tạo từ configmap 

kubectl get secret nginx-ssl-secret -n app-namespace21 -o jsonpath='{.data.tls\.crt}' | base64 -d > ca.crt

kubectl create configmap nginx-ca-cert --from-file=ca.crt=ca.crt -n app-namespace21

---

apiVersion: gateway.networking.k8s.io/v1

kind: BackendTLSPolicy

metadata:

  name: nginx-backend-tls-policy

  namespace: app-namespace21

spec:

  targetRefs:

  - group: ""

    kind: Service

    name: nginx-service-https

  validation:

    caCertificateRefs:

    - group: ""

      kind: ConfigMap

      name: nginx-ca-cert

    hostname: test.tuan.name.vn



Nginx mTLS

  A. Nginx as SERVER with mTLS Trong  hệ thống nội bộ, ca certificate thường dùng chung cả cho server và client. Nhưng nếu bảo mật hơn, có t...