Mục lục
- Mục Tiêu Bài Học
- bind Directive — Interface Nào Redis Lắng Nghe
- Default bind Thay Đổi Qua Các Phiên Bản
- protected-mode — Safety Net Khi bind Sai
- Error Message Khi protected-mode Trigger
- Khi Nào Tắt protected-mode
- Pattern Cấu Hình An Toàn Cho Production
- Multi-Interface Binding
- Docker & Kubernetes — Binding Trong Container
- Xác Minh Với netstat
- port Directive, Unix Socket & TLS Port
- IPv6 Considerations
- Defense In Depth — Không Chỉ Dựa Vào bind
- Anti-patterns & Common Mistakes
- Tổng Kết & Quiz
Mục Tiêu Bài Học
- Hiểu
binddirective quyết định interface nào Redis lắng nghe TCP connection, và ý nghĩa thực tế của từng giá trị (127.0.0.1, IP cụ thể,0.0.0.0). - Nắm sự thay đổi default
bindgiữa Redis < 3.2 và Redis ≥ 3.2, tại sao điều đó quan trọng về security. - Hiểu
protected-modehoạt động như thế nào, khi nào nó trigger, và điều kiện để nó kích hoạt. - Biết pattern cấu hình an toàn cho production server, Docker container và Kubernetes pod.
- Xác minh cấu hình bằng
netstatvà biết ký hiệu nào là "tốt" hay "nguy hiểm". - Nhận diện các anti-pattern dẫn đến Redis bị expose ngoài ý muốn.
bind Directive — Interface Nào Redis Lắng Nghe
Một server thường có nhiều network interface: loopback (127.0.0.1), internal NIC (10.0.x.x hay 192.168.x.x), public NIC (203.x.x.x). bind trong redis.conf chỉ định Redis chỉ chấp nhận kết nối TCP đến trên các interface (địa chỉ IP) được liệt kê. Kết nối đến interface không có trong bind sẽ bị từ chối ngay ở tầng kernel, trước khi Redis thậm chí nhìn thấy request.
# Chỉ localhost — process trên cùng máy mới kết nối được
bind 127.0.0.1
# Một IP nội bộ cụ thể — chỉ traffic đến interface đó
bind 192.168.1.10
# Tất cả interface — bao gồm cả public NIC (NGUY HIỂM nếu không có firewall)
bind 0.0.0.0
# Nhiều địa chỉ cùng lúc — localhost + internal
bind 127.0.0.1 192.168.1.10
# Redis ≥ 3.2 default (localhost IPv4 + IPv6 loopback, dấu - = optional nếu IPv6 không có)
bind 127.0.0.1 -::1
Dấu - trước địa chỉ (ví dụ -::1) có nghĩa là Redis sẽ không fail khi khởi động nếu interface đó không tồn tại — đây là syntax từ Redis 6.2+.
Khi bind không có trong redis.conf hoặc bị comment out, hành vi phụ thuộc phiên bản — xem mục tiếp theo.
Default bind Thay Đổi Qua Các Phiên Bản
Đây là thay đổi security quan trọng nhất trong lịch sử cấu hình Redis:
| Phiên bản | Default khi không có bind | Hậu quả |
|---|---|---|
| Redis < 3.2 | Bind tất cả interface (tương đương 0.0.0.0) |
Redis lắng nghe trên cả public IP nếu có; rất nhiều instance bị compromise vì không ai cấu hình bind |
| Redis ≥ 3.2 | bind 127.0.0.1 -::1 |
Chỉ chấp nhận kết nối từ localhost; an toàn mặc định |
Redis 3.2 được release năm 2016. Trước đó, nhiều Redis instance bị expose Internet do admin không thêm bind vào config, đặc biệt trên VPS cloud. Shodan từng index hàng chục nghìn Redis instance công khai truy cập được mà không cần mật khẩu.
Nếu bạn đang vận hành Redis < 3.2 (hiếm nhưng vẫn tồn tại trong legacy system), cần kiểm tra bind ngay. Redis 7.x (phiên bản hiện tại tính đến 2025) giữ default 127.0.0.1 -::1.
protected-mode — Safety Net Khi bind Sai
protected-mode được thêm vào Redis 3.2 cùng lúc với việc đổi default bind. Nó là một lớp kiểm tra logic: khi Redis thấy kết nối đến từ địa chỉ không phải loopback, nó sẽ kiểm tra ba điều kiện:
- Có bind cụ thể không? (không phải
0.0.0.0/ mọi interface) - Có
requirepass(mật khẩu AUTH) không? - Có TLS không?
Nếu cả ba đều không, protected-mode yes sẽ từ chối kết nối với error message rõ ràng. Logic này bảo vệ trường hợp ai đó vô tình cấu hình bind 0.0.0.0 mà quên mất AUTH.
# Default từ Redis 3.2+
protected-mode yes
Lưu ý: protected-mode không thay thế bind. Nó chỉ là kiểm tra phụ khi bind không được cấu hình chặt chẽ. Khi Redis đã bind đúng interface nội bộ và có AUTH, protected-mode trở thành lớp dư nhưng vẫn nên để yes.
Error Message Khi protected-mode Trigger
Khi bạn thử kết nối Redis từ xa và protected-mode chặn, bạn sẽ nhận được:
$ redis-cli -h 203.0.113.10
203.0.113.10:6379> ping
(error) DENIED Redis is running in protected mode because protected mode is enabled,
no bind address was specified, no authentication password is requested to clients.
In this mode connections are only accepted from the loopback interface.
If you want to connect from external computers to Redis you may adopt one of the
following solutions:
1) Just disable protected mode sending the command 'CONFIG SET protected-mode no' from the loopback interface
by connecting to Redis from the same host the server is running, however MAKE SURE Redis is not publicly
accessible from internet if you do so. Use CONFIG REWRITE to make this change permanent.
2) Alternatively you can just disable the protected mode by editing the Redis configuration file, and setting
the protected mode option to 'no', and then restarting the server.
3) If you started the server manually just for testing, restart it with the '--protected-mode no' parameter.
4) Setup a bind address or an authentication password. NOTE: You only need to do one of the following:
- Add a bind address: bind 127.0.0.1
- Or set an authentication password: requirepass <password>
Error message này rất rõ ràng và gợi ý đúng hướng. Điểm cần chú ý: message khuyên thêm bind 127.0.0.1 hoặc requirepass, không khuyên tắt protected-mode mà không làm gì khác. Nếu bạn đọc message kỹ, giải pháp 1 và 2 (disable protected-mode) được kèm cảnh báo "MAKE SURE Redis is not publicly accessible".
Khi Nào Tắt protected-mode
Có thể set protected-mode no một cách an toàn khi đã có ít nhất một trong các biện pháp sau:
- Bind specific interface:
bind 10.0.1.50(không phải0.0.0.0) — Redis chỉ nghe trên internal IP. - AUTH password:
requirepass StrongPassword123!— mọi kết nối đều phải authenticate. - TLS:
tls-port 6379với cert/key — kết nối không xác thực không thể dùng được.
Trong thực tế production, bạn thường có cả ba. Khi đó protected-mode không còn thêm giá trị thực sự, nhưng vẫn nên để yes như một safety net phòng trường hợp vô tình tháo một trong ba lớp kia.
# Không nên tắt protected-mode như thế này:
protected-mode no
# (không có bind cụ thể, không AUTH, không TLS — Redis hoàn toàn mở)
# Ổn nếu:
protected-mode no
bind 10.0.1.50 # chỉ nghe internal
requirepass Str0ngP@ss # AND có password
Pattern Cấu Hình An Toàn Cho Production
Một production server thường có hai network interface: public NIC (eth0) và private/VPC NIC (eth1). Redis chỉ nên nghe trên private NIC:
# redis.conf — production server trong VPC
# Listen only on internal VPC IP
bind 10.0.1.50
# Protected mode safety net (giữ yes dù đã bind specific)
protected-mode yes
# Mandatory AUTH — bài 113 đi sâu hơn
requirepass StrongPassword123!
# Standard port (có firewall ở tầng OS/cloud phía trước)
port 6379
# Disable dangerous commands (bài sau trong module này)
# rename-command CONFIG ""
# rename-command FLUSHALL ""
Với cấu hình này:
- Traffic từ public IP (
eth0) bị từ chối ở tầng OS — Redis không nghe trên interface đó. - Ngay cả khi firewall misconfigured, protected-mode + requirepass tạo thêm hai rào cản.
- App server cùng VPC kết nối qua
10.0.1.50:6379bình thường.
Multi-Interface Binding
Một số trường hợp Redis cần nghe trên nhiều interface cùng lúc:
- App traffic: app server kết nối qua internal VPC subnet (
10.0.1.0/24). - Replica traffic: replica kết nối qua cùng internal subnet.
- Monitoring: Prometheus Redis exporter chạy trên management VLAN riêng (
10.0.2.0/24).
# Listen trên cả app subnet VIP và management subnet VIP
bind 10.0.1.50 10.0.2.50
# Không cần 0.0.0.0 — vẫn không expose public interface
Cần đảm bảo cả hai subnet đều được kiểm soát bởi firewall/Security Group. Management VLAN thường restricted hơn app subnet: chỉ IP của monitoring server mới được phép kết nối vào port 6379 trên management interface.
Docker & Kubernetes — Binding Trong Container
Container thay đổi cách tư duy về bind: trong một Docker container, network namespace độc lập. bind 0.0.0.0 bên trong container chỉ có nghĩa là "tất cả interface của container đó", không phải tất cả interface của host. Việc container có bị expose ra ngoài hay không phụ thuộc vào cấu hình Docker network và port mapping, không phải Redis bind.
Docker Compose
# docker-compose.yml
services:
redis:
image: redis:7
command: redis-server --requirepass ${REDIS_PASSWORD} --bind 0.0.0.0
networks:
- app-internal # Docker internal overlay network
# Không có ports: mapping → container không expose ra host
# App service cùng network kết nối được qua tên service "redis"
app:
image: myapp:latest
environment:
REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379
networks:
- app-internal
networks:
app-internal:
driver: bridge
Điểm mấu chốt: không có dòng ports: "6379:6379". Nếu không map port ra host, Redis chỉ tiếp cận được từ container cùng Docker network. bind 0.0.0.0 ở đây an toàn vì network isolation do Docker cung cấp. Dù vậy, requirepass vẫn cần thiết.
Kubernetes NetworkPolicy
Trong K8s, bind 0.0.0.0 là bình thường vì Pod IP thay đổi liên tục và không biết trước. Security thay vào đó được đặt ở tầng NetworkPolicy:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: redis-allow-app
namespace: production
spec:
podSelector:
matchLabels:
app: redis
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: web
ports:
- protocol: TCP
port: 6379
NetworkPolicy này chỉ cho phép Pod có label app: web kết nối vào Redis port 6379. Mọi Pod khác, kể cả trong cùng namespace, bị từ chối ở tầng Kubernetes network plugin (Calico, Cilium, v.v.). Redis vẫn cần requirepass vì NetworkPolicy không thay thế authentication.
Xác Minh Với netstat
Sau khi cấu hình, luôn verify Redis đang listen đúng interface bằng netstat hoặc ss:
# netstat (cũ hơn, có thể cần apt install net-tools)
netstat -tlnp | grep 6379
# ss (hiện đại hơn, có sẵn trên hầu hết Linux)
ss -tlnp | grep 6379
Đọc output:
# BAD — Redis nghe trên tất cả interface (bao gồm public IP)
tcp 0.0.0.0:6379 0.0.0.0:* LISTEN 1234/redis-server
# GOOD — chỉ localhost
tcp 127.0.0.1:6379 0.0.0.0:* LISTEN 1234/redis-server
# GOOD — chỉ internal VPC IP
tcp 10.0.1.50:6379 0.0.0.0:* LISTEN 1234/redis-server
# GOOD — localhost + internal
tcp 127.0.0.1:6379 0.0.0.0:* LISTEN 1234/redis-server
tcp 10.0.1.50:6379 0.0.0.0:* LISTEN 1234/redis-server
Ngoài netstat, có thể test từ bên ngoài để xác nhận kết nối bị từ chối:
# Từ máy ngoài — phải fail
redis-cli -h 203.0.113.10 -p 6379 ping
# → Could not connect to Redis at 203.0.113.10:6379: Connection refused
# Từ trong server (internal IP) — phải thành công
redis-cli -h 10.0.1.50 -p 6379 -a StrongPassword123! ping
# → PONG
port Directive, Unix Socket & TLS Port
port directive
port 6379 # Default Redis port
port 0 # Disable TCP hoàn toàn (chỉ dùng Unix socket hoặc TLS)
Đôi khi có ý kiến đổi port sang non-standard (ví dụ 6380, 16379) để giảm scan từ script kiddies. Điều này có tác dụng rất hạn chế: các port scanner như nmap sẽ vẫn tìm ra. Đổi port không phải security, chỉ là thay đổi nhỏ trong ops. Không nên coi đây là biện pháp bảo mật.
Unix socket — chỉ local
Khi app và Redis cùng chạy trên một máy (ví dụ: Redis Sentinel + Redis instance, hoặc PHP-FPM + Redis trên cùng VM), Unix domain socket loại bỏ hoàn toàn network layer:
# redis.conf
unixsocket /var/run/redis/redis.sock
unixsocketperm 700 # Chỉ owner (redis user) đọc/ghi được
port 0 # Disable TCP — chỉ socket
# Kết nối qua socket
redis-cli -s /var/run/redis/redis.sock ping
File permission (700, 770) kiểm soát ai được kết nối: chỉ process chạy với cùng user/group mới mở được file socket. Phù hợp cho Sentinel-to-Redis local communication.
TLS port (Redis 6+)
port 0 # Disable plain TCP
tls-port 6379 # Chỉ chấp nhận TLS connection
tls-cert-file /etc/redis/redis.crt
tls-key-file /etc/redis/redis.key
tls-ca-cert-file /etc/redis/ca.crt
TLS được đề cập chi tiết ở bài 115. Điểm cần biết ở đây: khi dùng TLS, connection không có cert hợp lệ bị từ chối ở tầng TLS handshake, trước khi Redis process xử lý command.
IPv6 Considerations
Nếu server có IPv6, cần chú ý:
# IPv6 localhost only
bind ::1
# All IPv6 interfaces (tương đương 0.0.0.0 cho IPv4)
bind ::
# Mix IPv4 localhost + IPv6 localhost
bind 127.0.0.1 ::1
# Redis default từ 3.2 (dấu - = optional — không fail nếu IPv6 không available)
bind 127.0.0.1 -::1
Một lỗi phổ biến: cấu hình bind 127.0.0.1 nhưng quên IPv6, sau đó app kết nối qua ::1 (IPv6 loopback) và không được chấp nhận. Nếu không dùng IPv6, tắt hẳn IPv6 trên OS hoặc chỉ bind 127.0.0.1 và đảm bảo app connect qua IPv4.
Defense In Depth — Không Chỉ Dựa Vào bind
bind chỉ là một lớp. Một hệ thống production cần nhiều lớp bảo vệ đồng thời:
| Lớp | Cơ chế | Phạm vi |
|---|---|---|
| Redis-level | bind + protected-mode |
Redis process quyết định nghe interface nào |
| OS-level | iptables / ufw |
Kernel filter traffic trước khi đến process |
| Cloud-level | Security Group (AWS/GCP) / NSG (Azure) | Network-level filter trước khi vào VM |
| Authentication | requirepass / ACL (bài 113–114) |
Ngay cả khi có kết nối, phải authenticate |
| Encryption | TLS (bài 115) | Traffic không bị nghe lén trên wire |
Chỉ dựa vào một lớp là sai lầm. Ví dụ: chỉ có firewall mà không có bind — nếu firewall rule bị misconfigured, Redis ngay lập tức bị expose. Chỉ có bind mà không có AUTH — kẻ tấn công từ bên trong VPC vẫn kết nối được tự do.
# Ví dụ iptables: chỉ cho phép 10.0.1.0/24 kết nối port 6379
iptables -A INPUT -p tcp --dport 6379 -s 10.0.1.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 6379 -j DROP
# ufw
ufw allow from 10.0.1.0/24 to any port 6379
ufw deny 6379
Anti-patterns & Common Mistakes
Các lỗi thường gặp dẫn đến Redis bị expose:
-
Xóa dòng bind mặc định trong redis.conf
Nhiều người comment out hoặc xóa dòngbind 127.0.0.1 -::1mà không thêm bind mới. Trên Redis < 3.2, hậu quả là bind all interfaces. Trên Redis ≥ 3.2, protected-mode sẽ chặn nhưng không phải giải pháp đúng. -
bind 0.0.0.0"cho tiện" rồi để đó
Thường xảy ra khi dev set up local test, sau đó cấu hình này bị push lên production.0.0.0.0kết hợp với Security Group misconfigured là công thức để Redis bị expose. -
Chỉ dựa vào bind mà không có firewall
Nếu Redis process bị exploit và attacker có thể thay đổi cấu hình runtime (CONFIG SET bind), bind không còn là biện pháp bảo vệ nữa. Cần có firewall độc lập ở tầng OS/cloud. -
Tắt protected-mode để "fix" kết nối trong test rồi quên re-enable
CONFIG SET protected-mode nohoặc thêmprotected-mode novào config khi debug, sau đó config này được commit và deploy production. -
Bind
127.0.0.1nhưng muốn remote kết nối được
Đây không phải lỗi bảo mật, nhưng là lỗi ops phổ biến: sau đó tắt protected-mode mà không thêm internal IP vào bind, khiến remote vẫn không kết nối được. -
Public cloud +
bind 0.0.0.0+ Security Group "mở tạm"
Security Group rule0.0.0.0/0:6379"mở tạm để test" rồi quên đóng lại. Kết hợp với bind0.0.0.0, Redis hoàn toàn public.
Tổng Kết & Quiz
Hai directive bind và protected-mode là lớp đầu tiên kiểm soát ai có thể mở TCP connection đến Redis. bind quyết định interface — nên luôn chỉ định explicit, không dựa vào default. protected-mode là safety net khi bind chưa đủ chặt. Cả hai cần kết hợp với firewall và AUTH để có bảo mật thực sự.
Checklist trước khi deploy Redis lên production:
bindđặt explicit với internal IP (không phải0.0.0.0, trừ container với network isolation).protected-mode yes.requirepasscó giá trị mạnh (bài 113).- Firewall OS và cloud Security Group chỉ cho phép app subnet đến port 6379.
- Verify bằng
netstat -tlnp | grep 6379và test connection từ ngoài.
Quiz
- Redis được cấu hình
bind 0.0.0.0,protected-mode yes, không córequirepass. Một client từ IP bên ngoài kết nối vào. Chuyện gì xảy ra? - Redis ≥ 3.2 không có dòng
bindnào trong redis.conf. Default là gì? Điều này khác gì so với Redis < 3.2? - Trong Docker Compose, Redis được cấu hình
bind 0.0.0.0nhưng không có dòngports:. Redis có bị expose ra máy host không? - Giải thích tại sao "đổi port Redis sang 16379 để tăng bảo mật" không phải biện pháp security thực sự.
- Bạn có một server với hai NIC:
eth0(public203.0.113.10) vàeth1(internal10.0.1.50). Viết cấu hìnhbind+protected-mode+requirepassđúng.
Đáp án gợi ý
- Connection bị từ chối với error "DENIED Redis is running in protected mode". Điều kiện trigger: bind all interfaces + không có AUTH + không có TLS → protected-mode chặn kết nối non-loopback.
- Default là
bind 127.0.0.1 -::1(chỉ loopback). Redis < 3.2 không có dòng bind → bind tất cả interfaces (tương đương0.0.0.0), dẫn đến nhiều instance bị expose Internet trước khi Redis 3.2 ra mắt năm 2016. - Không. Không có
ports:mapping nghĩa là port chỉ mở trong Docker network nội bộ. Container từ service khác cùng network kết nối được, máy host và bên ngoài không thể kết nối trực tiếp vào container. - Port scanner (nmap, masscan) quét toàn bộ port range, không chỉ 6379. Port 16379 sẽ bị phát hiện với banner Redis như bình thường. Đổi port chỉ giảm noise từ scanner amateur, không ngăn được targeted attack. Security thực sự đến từ firewall, authentication và encryption, không phải port number.
bind 10.0.1.50(chỉ internal NIC, không bind203.0.113.10),protected-mode yes,requirepass StrongPassword123!. Tùy nhu cầu có thể thêmbind 10.0.1.50 127.0.0.1nếu cần local tool (redis-cli) trên máy chủ đó kết nối qua localhost.
Bài tiếp theo
Bài 113 đi vào AUTH password: requirepass, masterauth cho replication, giới hạn của single-password và ACL (Redis 6+).
