Cổng Linux là gì và service lắng nghe thế nào
Hiểu cổng 1–65535, TCP và UDP, rồi dùng ss để xem service nào đang listening. Phân biệt cổng local với cổng public trước khi mở firewall.
Cổng thực sự là gì
Cổng là một số cho phép một server chạy nhiều service cùng lúc mà traffic của chúng không bị lẫn vào nhau. VPS của bạn có một địa chỉ IP, nhưng có thể đồng thời chạy web server, SSH server và database. Khi một packet đến, operating system cần biết packet đó dành cho service nào. Số cổng là thông tin dùng để xác định điều này. Web traffic đi đến cổng 443, SSH đi đến cổng 22. Địa chỉ IP đưa packet đến server của bạn; cổng đưa packet đến đúng chương trình trên server đó.
Cổng là một số 16-bit: 16 bit tạo ra các giá trị từ 0 đến 65535, và 0 được dành riêng, nên trên thực tế cổng chạy từ 1 đến 65535. Các cổng dưới 1024 là cổng “well-known” và cần root để mở. Vì vậy, các service tiêu chuẩn thường dùng những cổng này: 22 cho SSH, 80 cho HTTP, 443 cho HTTPS, 53 cho DNS. Các cổng trên 1024 có thể được chương trình thông thường sử dụng.
Có 2 loại là TCP và UDP. Số cổng của loại này độc lập với cùng số đó ở loại kia. TCP là protocol hướng kết nối được hầu hết service sử dụng, chẳng hạn như SSH, HTTP và database. UDP không dùng kết nối và được dùng cho những thứ như DNS và một số VPN. Khi mở firewall, bạn thường phải chỉ rõ loại protocol, ví dụ 22/tcp.
Listening nghĩa là một chương trình đang chờ trên một cổng
Một chương trình muốn nhận kết nối sẽ yêu cầu kernel “listen” trên một cổng. Từ thời điểm đó, cổng này được mở trên máy và chương trình sẽ xử lý mọi kết nối gửi đến đó, tùy theo firewall. Cổng không có chương trình nào đang listen sẽ từ chối kết nối. Vì vậy, câu hỏi đầu tiên trong mọi lần kiểm tra bảo mật là: chương trình nào đang listen và listen ở đâu.
Lệnh dùng để kiểm tra là ss:
sudo ss -tlnpCác flag cho biết TCP (t), chỉ hiển thị socket đang listen (l), hiển thị cổng ở dạng số (n, để bạn thấy 22 thay vì ssh), và hiển thị process đang sở hữu socket (p). Kết quả thường có dạng:
State Recv-Q Local Address:Port Process
LISTEN 0 0.0.0.0:22 sshd
LISTEN 0 127.0.0.1:5432 postgres
LISTEN 0 [::]:80 nginxCột Process cho biết chương trình nào sở hữu từng cổng. Đây là cách xác định một chương trình mà bạn không định để chạy nhưng vẫn đang chạy.
Cột Local Address mới là phần quan trọng
Đọc kỹ địa chỉ đứng trước mỗi port, vì địa chỉ đó quyết định ai có thể truy cập service. Bạn sẽ thường xuyên gặp 3 trường hợp sau.
0.0.0.0:22 có nghĩa là “listen trên mọi địa chỉ IPv4 mà máy này có”, bao gồm cả địa chỉ public. Service có thể được truy cập từ Internet qua IPv4 nếu firewall cho phép.
[::]:80 có ý nghĩa tương tự với IPv6: listen trên mọi địa chỉ IPv6, bao gồm cả địa chỉ public. Nhiều chương trình bind vào đây theo mặc định, và trên Linux, socket :: thường cũng nhận kết nối IPv4.
127.0.0.1:5432 có nghĩa là “chỉ listen trên loopback”. 127.0.0.1 là địa chỉ máy dùng để kết nối với chính nó và không thể được định tuyến từ nơi khác. Service bind vào địa chỉ này chỉ có thể được truy cập từ chính server đó, không bao giờ từ Internet, bất kể firewall được cấu hình thế nào. Dòng Postgres ở trên an toàn ngay từ thiết kế.
Quy tắc thực tế rất rõ: database, cache hoặc admin panel chỉ được các ứng dụng của bạn sử dụng nên bind vào 127.0.0.1, không phải 0.0.0.0. Nếu service không bao giờ listen trên địa chỉ public thì attacker không có endpoint để truy cập.
Listening không đồng nghĩa với có thể truy cập
Có một điểm phân biệt nữa giúp tránh rất nhiều nhầm lẫn. Một cổng mở trên máy, tức là có chương trình đang listen, khác với việc cổng đó có thể truy cập từ bên ngoài, tức là firewall cho phép truy cập. Cả hai điều kiện đều phải đúng thì remote client mới kết nối được. Hai lỗi này cũng biểu hiện khác nhau ở phía client: connection bị từ chối nghĩa là không có tiến trình nào đang listen, còn connection bị treo rồi timeout thường có nghĩa là firewall đã drop packet. Đây chính là ý nghĩa của hai lỗi SSH đó.
Vì vậy, có hai lớp kiểm soát. Bạn quyết định service nào được listen bằng cách đặt bind address cho từng service. Bạn quyết định bên ngoài có thể truy cập gì bằng firewall. Một server được vận hành đúng cách dùng cả hai: service chỉ bind vào nơi cần thiết, còn firewall deny mọi thứ không được allow rõ ràng. Đọc danh sách các tiến trình đang listen bằng ss là bước một; quyết định firewall cho phép gì là bước hai, được trình bày trong bài nhập môn về firewall với UFW.
Có một điểm tinh tế thường gây lỗi. IPv4 và IPv6 là hai giao thức riêng, nên firewall chỉ áp dụng cho IPv4 sẽ để các service [::] mở trên IPv6. Lỗi cụ thể này được trình bày trong bài riêng: bẫy firewall IPv6.
FAQ
Làm thế nào để xem các cổng đang mở trên máy chủ Linux?
Chạy sudo ss -tlnp cho TCP hoặc sudo ss -ulnp cho UDP. Lệnh này liệt kê mọi socket đang listen, gồm cổng, địa chỉ local mà socket bind vào và process sở hữu socket đó. Xem cột Local Address: 0.0.0.0 hoặc [::] nghĩa là service được expose ra network, còn 127.0.0.1 nghĩa là service chỉ listen trên chính máy đó.
0.0.0.0 và 127.0.0.1 khác nhau như thế nào?
0.0.0.0 nghĩa là "listen trên mọi địa chỉ IPv4", bao gồm cả địa chỉ public, nên có thể truy cập service từ network. 127.0.0.1 là loopback, địa chỉ máy dùng để tự giao tiếp với chính nó. Địa chỉ này không thể được route từ nơi khác, nên service bind vào đó chỉ có thể truy cập locally. Hãy bind các service nội bộ vào 127.0.0.1 để chúng không bao giờ bị expose.
Tôi có cần mở cổng trên firewall để service hoạt động không?
Chỉ cần mở cổng cho các service phải được truy cập từ máy khác. Service bind vào 127.0.0.1 không cần firewall rule vì không có nguồn bên ngoài nào có thể truy cập nó. Service trên 0.0.0.0 hoặc [::] cần firewall rule để cho phép traffic, và mặc định nên bị deny cho đến khi bạn thêm rule.
Cổng TCP và cổng UDP khác nhau như thế nào?
TCP hoạt động dựa trên connection và được hầu hết service sử dụng, chẳng hạn như SSH, web server và database. UDP không dùng connection và được DNS cùng một số VPN sử dụng. Cùng một số trên TCP và UDP là các cổng riêng biệt, nên 53/tcp và 53/udp là hai cổng khác nhau. Khi viết firewall rule, bạn phải chỉ rõ protocol, ví dụ 22/tcp.