Bảo mật Apache Kafka trên Production: Tăng cường an toàn với mã hóa TLS, SASL/SCRAM và ACL

Security tutorial - IT technology blog
Security tutorial - IT technology blog

Vì sao các cụm Kafka không bảo mật thất bại trên Production

Đội ngũ của chúng tôi đã phải trả giá đắt để rút ra bài học này. Vào một sáng thứ Ba, lưu lượng network egress bất ngờ tăng vọt từ mức thông thường 15 MB/s lên gần 400 MB/s. Một pod môi trường staging bên trong VPC khởi động và trỏ thẳng vào port plaintext mặc định 9092 của broker — sự cố lẽ ra đã được ngăn chặn nếu triển khai chính sách mạng Kubernetes với Cilium để cô lập lưu lượng. Do broker không bật mã hóa, không yêu cầu xác thực và không có kiểm soát truy cập, container thử nghiệm duy nhất đó đã bắt đầu kéo dữ liệu giao dịch thẻ tín dụng chưa được che (unmasked) trực tiếp ngay trên đường truyền mạng.

Mặc định khi cài đặt, Apache Kafka ưu tiên sự tiện lợi cho lập trình viên. Mọi message truyền qua mạng đều ở dạng plaintext. Bất kỳ host nào định tuyến được lưu lượng đến port 9092 đều có thể đọc dữ liệu từ bất kỳ topic nào, đẩy các bản ghi bị lỗi hoặc âm thầm xóa các partition. Tương tự như nguyên tắc bảo mật Redis Server với ACL và TLS, để bảo vệ một hệ thống triển khai production, cần thiết lập ba lớp bảo vệ riêng biệt:

  • Transport Layer Security (TLS): Mã hóa lưu lượng giữa các broker với nhau và giữa client với broker. Trên các CPU hiện đại hỗ trợ tập lệnh AES-NI, việc này thường chỉ làm tăng chưa đến 3% mức tải CPU.
  • SASL/SCRAM-512: Xác thực client bằng cơ chế băm challenge-response có salt. Giải pháp này giúp tránh gửi mật khẩu thô qua mạng và loại bỏ gánh nặng vận hành phức tạp của hạ tầng Kerberos/KDC.
  • Access Control Lists (ACLs): Giới hạn các principal đã xác thực chỉ được phép thực hiện những thao tác cụ thể trên các topic và consumer group được chỉ định.

Bước 1: Khởi tạo Certificate Authority và Keystore

Trước khi chỉnh sửa các file cấu hình Kafka, bạn cần thiết lập hạ tầng khóa công khai (PKI). Chúng ta sẽ khởi tạo một Certificate Authority (CA) chuyên dụng và cấp phát các keystore đã được ký kèm theo Subject Alternative Name (SAN) nhằm vượt qua bước kiểm tra hostname nghiêm ngặt của JVM hiện đại.

1. Tạo Certificate Authority nội bộ

# Tạo key và root certificate cho CA nội bộ có hiệu lực trong 3 năm
openssl req -new -x509 -keyout ca-key -out ca-cert -days 1095 \
  -subj "/CN=Internal-Kafka-CA/OU=Infra/O=ItFromZero/C=US" \
  -nodes

2. Tạo Keystore cho Broker kèm SAN

Thực hiện các bước sau trên từng node broker (ví dụ: kafka1.internal tại địa chỉ IP 10.0.1.10). Các phiên bản Java hiện đại mặc định bắt buộc xác thực hostname qua TLS, do đó việc bỏ qua extension SAN sẽ làm hỏng quá trình bắt tay (handshake) của client.

# 1. Tạo keystore cho broker kèm extension SAN
keytool -keystore server.keystore.jks -alias kafka1.internal \
  -validity 365 -genkey -keyalg RSA -keysize 2048 \
  -storepass "KeystoreSecret123!" -keypass "KeystoreSecret123!" \
  -dname "CN=kafka1.internal,OU=Infra,O=ItFromZero,C=US" \
  -ext SAN=DNS:kafka1.internal,IP:10.0.1.10

# 2. Xuất Certificate Signing Request (CSR)
keytool -keystore server.keystore.jks -alias kafka1.internal \
  -certreq -file cert-file -storepass "KeystoreSecret123!" \
  -ext SAN=DNS:kafka1.internal,IP:10.0.1.10

# 3. Ký CSR bằng CA nội bộ
openssl x509 -req -CA ca-cert -CAkey ca-key -in cert-file \
  -out cert-signed -days 365 -CAcreateserial \
  -extfile <(printf "subjectAltName=DNS:kafka1.internal,IP:10.0.1.10")

# 4. Import chứng chỉ CA vào keystore
keytool -keystore server.keystore.jks -alias CARoot \
  -import -noprompt -file ca-cert -storepass "KeystoreSecret123!"

# 5. Import chứng chỉ đã ký
keytool -keystore server.keystore.jks -alias kafka1.internal \
  -import -noprompt -file cert-signed -storepass "KeystoreSecret123!"

3. Tạo Truststore cho Broker và Client

Các broker và producer đều phải tin cậy root CA đã ký chứng chỉ cho cụm của bạn.

# Tạo truststore chứa chứng chỉ CA
keytool -keystore server.truststore.jks -alias CARoot \
  -import -noprompt -file ca-cert -storepass "TruststoreSecret123!"

# Phân phối một bản sao cho môi trường runtime của client
cp server.truststore.jks client.truststore.jks

Bước 2: Tăng cường bảo mật cấu hình Broker

1. Cấu hình Listener trên Broker

Chỉnh sửa file cấu hình broker (thường là /etc/kafka/server.properties). Chúng ta sẽ cấu hình port 9093 cho mã hóa mutual TLS kết hợp với xác thực SASL/SCRAM, đồng thời đóng hoàn toàn port 9092.

# Listener và protocol
listeners=SASL_SSL://kafka1.internal:9093
advertised.listeners=SASL_SSL://kafka1.internal:9093
listener.security.protocol.map=SASL_SSL:SASL_SSL
inter.broker.listener.name=SASL_SSL
sasl.mechanism.inter.broker.protocol=SCRAM-SHA-512

# Keystore và Truststore TLS
ssl.keystore.location=/var/private/ssl/server.keystore.jks
ssl.keystore.password=KeystoreSecret123!
ssl.key.password=KeystoreSecret123!
ssl.truststore.location=/var/private/ssl/server.truststore.jks
ssl.truststore.password=TruststoreSecret123!
ssl.endpoint.identification.algorithm=HTTPS

# Xác thực
sasl.enabled.mechanisms=SCRAM-SHA-512

# Phân quyền (KRaft StandardAuthorizer / Kafka 3.x+)
authorizer.class.name=org.apache.kafka.metadata.authorizer.StandardAuthorizer
allow.everyone.if.no.acl.found=false
super.users=User:admin

2. Cấu hình file JAAS cho Broker

Broker xác thực các lượt bắt tay gửi đến từ client thông qua Java Authentication and Authorization Service (JAAS). Lưu file này tại /etc/kafka/kafka_server_jaas.conf. Khi quản lý các cụm production, việc tạo mật khẩu mạnh và an toàn là yêu cầu bắt buộc. Tôi thường dùng công cụ tạo mật khẩu tại toolcraft.app/vi/tools/security/password-generator cho mật khẩu máy chủ — công cụ này chạy hoàn toàn trên trình duyệt, không có dữ liệu nào bị gửi qua mạng, rất đáng tin cậy cho việc thiết lập hạ tầng offline.

KafkaServer {
    org.apache.kafka.common.security.scram.ScramLoginModule required
    username="admin"
    password="e9V#kP9$mR2@zQ7!xT5";
};

Client {
    org.apache.kafka.common.security.scram.ScramLoginModule required
    username="admin"
    password="e9V#kP9$mR2@zQ7!xT5";
};

Trỏ tiến trình Kafka tới file JAAS của bạn trước khi khởi động daemon:

export KAFKA_OPTS="-Djava.security.auth.login.config=/etc/kafka/kafka_server_jaas.conf"
systemctl restart kafka

3. Khởi tạo thông tin xác thực người dùng qua SCRAM-SHA-512

Khi broker đã lắng nghe qua TLS, hãy đăng ký thông tin xác thực của người dùng trực tiếp vào metadata của cụm. Thực hiện bootstrap bằng file thông tin quản trị viên (/tmp/admin-bootstrap.properties):

# Đăng ký tài khoản admin cho cụm
kafka-configs.sh --bootstrap-server kafka1.internal:9093 \
  --entity-type users --entity-name admin \
  --alter --add-config 'SCRAM-SHA-512=[password=e9V#kP9$mR2@zQ7!xT5]' \
  --command-config /tmp/admin-bootstrap.properties

# Đăng ký service account cho ứng dụng
kafka-configs.sh --bootstrap-server kafka1.internal:9093 \
  --entity-type users --entity-name payments-worker \
  --alter --add-config 'SCRAM-SHA-512=[password=uP4!vL9$xN2@bM7^kQ1]' \
  --command-config /tmp/admin-bootstrap.properties

4. Cấp quyền truy cập chi tiết bằng ACL

Vì cấu hình allow.everyone.if.no.acl.found=false đang bật, mọi topic đều bị từ chối truy cập theo mặc định. Cấp quyền chính xác cho payments-worker để ghi các outbound event và đọc các inbound task:

# Cho phép payments-worker ghi vào topic 'payment-events'
kafka-acls.sh --bootstrap-server kafka1.internal:9093 \
  --command-config /tmp/admin-bootstrap.properties \
  --add --allow-principal User:payments-worker \
  --operation Write --operation Describe \
  --topic payment-events \
  --resource-pattern-type literal

# Cho phép payments-worker consume topic 'settlement-tasks' trong 'payments-group'
kafka-acls.sh --bootstrap-server kafka1.internal:9093 \
  --command-config /tmp/admin-bootstrap.properties \
  --add --allow-principal User:payments-worker \
  --operation Read --operation Describe \
  --topic settlement-tasks \
  --group payments-group

Bước 3: Kiểm thử và Giám sát

Song song với việc kiểm tra bảo mật Linux với Lynis trên máy chủ, hãy kiểm tra hệ thống phòng thủ của bạn bằng các lệnh producer thực tế trước khi áp dụng các thay đổi lên môi trường production.

1. Kiểm thử lưu lượng được cấp quyền

Khai báo cấu hình client trong client-payments.properties:

security.protocol=SASL_SSL
sasl.mechanism=SCRAM-SHA-512
ssl.truststore.location=/var/private/ssl/client.truststore.jks
ssl.truststore.password=TruststoreSecret123!
sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required \
    username="payments-worker" \
    password="uP4!vL9$xN2@bM7^kQ1";

Gửi một message thử nghiệm tới topic đã được cấp phép:

echo '{"tx_id": "98412", "amount": 120.50}' | kafka-console-producer.sh \
  --bootstrap-server kafka1.internal:9093 \
  --topic payment-events \
  --producer.config client-payments.properties

2. Xác nhận các yêu cầu không hợp lệ bị chặn

Thao tác gửi dữ liệu tới topic chưa được phân quyền như audit-logs phải bị từ chối rõ ràng:

echo 'sự kiện thử nghiệm' | kafka-console-producer.sh \
  --bootstrap-server kafka1.internal:9093 \
  --topic audit-logs \
  --producer.config client-payments.properties

Broker sẽ từ chối batch ngay lập tức:

[2026-11-07 02:45:12,104] ERROR Lỗi khi gửi message tới topic audit-logs với key: null
org.apache.kafka.common.errors.TopicAuthorizationException: Không có quyền truy cập các topic: [audit-logs]

3. Các Metric Production cần theo dõi trong Prometheus

  • kafka.server:type=RequestMetrics,name=ErrorsPerSec,error=TOPIC_AUTHORIZATION_FAILED: Bất kỳ tỷ lệ nào duy trì trên 0 req/giây đều cho thấy thông tin xác thực bị cấu hình sai hoặc có hành vi quét dò mạng trái phép.
  • kafka.server:type=RequestMetrics,name=ErrorsPerSec,error=SASL_AUTHENTICATION_FAILED: Các mức tăng vọt đột biến là dấu hiệu của tấn công brute-force xác thực hoặc mật khẩu service account đã lỗi thời.
  • ssl-certificate-expiration-date: Kích hoạt cảnh báo trước 30 ngày khi chứng chỉ của broker hết hạn nhằm ngăn chặn sự cố gián đoạn bắt tay TLS.

Việc thực thi TLS, SCRAM-512 và chính sách phân quyền ACL theo nguyên tắc đặc quyền tối thiểu sẽ biến một event bus mở thành một tầng truyền thông điệp an toàn, sẵn sàng đáp ứng các tiêu chuẩn kiểm toán tuân thủ kết hợp cùng hệ thống giám sát sự kiện với Linux Auditd.

Share: