Đổi cổng SSH trên Rocky Linux với SELinux, firewalld
Đổi cổng SSH trên Rocky Linux hoặc AlmaLinux đúng thứ tự: mở firewalld, gán SELinux port label, sửa sshd_config để không mất session hiện tại.
Vì sao đổi cổng SSH ở đây cần 3 bước
Để đổi cổng SSH trên Rocky Linux, AlmaLinux, CentOS Stream hoặc Fedora, chỉ sửa một chỗ là chưa đủ. 3 hệ thống riêng biệt cùng quyết định liệu kết nối đến cổng mới có hoạt động hay không. firewalld quyết định packet có đến được máy hay không. SELinux quyết định sshd có được phép bind vào số cổng đó hay không. sshd_config quyết định daemon yêu cầu sử dụng cổng nào. Bỏ qua bước SELinux thì daemon sẽ không khởi động. Bỏ qua bước firewalld thì daemon vẫn khởi động và listen, nhưng không ai kết nối được.
Trên Ubuntu, công việc tương tự chỉ cần sửa 1 chỗ và restart, vì Ubuntu dùng AppArmor thay cho SELinux và không cài sẵn profile giới hạn các cổng mà sshd được phép bind. Nếu ufw đang chạy, bạn thêm 1 rule. Toàn bộ khác biệt là như vậy. Họ RHEL cài sẵn firewalld đang chạy và SELinux ở chế độ enforcing trên hệ thống mới cài, đồng thời cả 2 đều kiểm soát số cổng.
Thực hiện theo thứ tự này để session hiện tại vẫn hoạt động trong suốt quá trình:
- Mở cổng mới trong firewalld, tạm thời vẫn để cổng 22 mở.
- Thêm SELinux label cho cổng mới bằng
semanage. - Đặt cổng trong cấu hình sshd.
- Restart
sshd, sau đó đăng nhập bằng cổng mới từ terminal thứ 2 trước khi đóng terminal thứ nhất.
Tìm web console của nhà cung cấp (VNC hoặc serial) trước khi bắt đầu và kiểm tra rằng bạn có thể đăng nhập qua đó. Console này là cách để truy cập lại máy nếu thay đổi xảy ra lỗi. Đổi cổng là một trong những lý do phổ biến nhất khiến tenant tự khóa quyền truy cập vào server mà họ vừa thuê.
Trước tiên, cài semanage
semanage là công cụ dùng để chỉnh sửa các thiết lập policy của SELinux. Bản cài đặt tối thiểu của Rocky Linux hoặc AlmaLinux không có công cụ này. Công cụ nằm trong policycoreutils-python-utils.
sudo dnf install -y policycoreutils-python-utilsChạy lệnh trước khi cài package đó sẽ trả về sudo: semanage: command not found. Đây là lúc nhiều người kết luận SELinux chưa được cài và bỏ qua bước này. SELinux đã được cài. Chỉ thiếu công cụ quản lý. Nếu cú pháp dnf còn mới với bạn, các lệnh tương đương trong dnf và apt sẽ liên hệ cú pháp này với những gì bạn đã biết.
Chọn một cổng và kiểm tra xem có tiến trình nào đang sử dụng không
Có thể dùng bất kỳ cổng TCP còn trống nào từ 1024 đến 65535. Hãy thực hiện 2 kiểm tra trước khi chọn một cổng:
sudo ss -tlnp | grep -w 2222
sudo semanage port -l | grep -w 2222Lệnh đầu tiên cho biết đã có tiến trình nào đang lắng nghe trên số cổng đó chưa. Lệnh thứ hai cho biết policy của SELinux đã gán cổng đó cho loại service khác chưa. Cổng còn trống sẽ không trả về kết quả nào ở cả hai lệnh. Nếu policy đã sử dụng cổng đó, semanage port -a ở bước 2 sẽ fail với ValueError: Port tcp/2222 already defined. Khi đó, hãy chọn số cổng khác.
Trong toàn bộ hướng dẫn này, ví dụ sử dụng cổng 2222. Tuy nhiên, đây cũng là cổng đầu tiên scanner thường thử sau cổng 22. Trên server thực tế, hãy chọn một cổng khó đoán hơn.
Bước 1: mở cổng trong firewalld
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-ports--permanent ghi rule vào file zone trên ổ đĩa nhưng không tác động đến firewall đang chạy. --reload nạp cấu hình trên ổ đĩa vào firewall đang chạy. Nếu bỏ qua bước reload, rule vẫn tồn tại nhưng không có tác dụng cho đến lần firewalld khởi động lại tiếp theo. Đây là một trong những nguyên nhân phổ biến nhất khiến toàn bộ quy trình này có vẻ thất bại mà không rõ lý do.
Tạm thời không thay đổi entry của service ssh. Entry đó giữ cho cổng 22 mở và là đường dự phòng khi bạn kiểm tra.
Bạn cũng nên kiểm tra control panel của nhà cung cấp. Nhiều nhà cung cấp chạy network firewall phía trước VPS, bên ngoài hệ điều hành. Vì vậy, cổng đã mở trong firewalld vẫn có thể bị chặn ở upstream. Hướng dẫn cơ bản về firewalld cho VPS giải thích về zone và sự khác nhau giữa cấu hình runtime và cấu hình permanent nếu bạn chưa quen với mô hình này.
Bước 2: gắn nhãn cho cổng trong SELinux
sudo semanage port -a -t ssh_port_t -p tcp 2222
sudo semanage port -l | grep ssh_port_t-a thêm một ánh xạ cổng mới. -t ssh_port_t là type mà các cổng SSH sử dụng. Lệnh thứ hai liệt kê mọi thứ mà ssh_port_t hiện bao phủ, để bạn xác nhận số cổng đã được thêm trước khi thay đổi daemon.
Vì sao SELinux chặn cổng
SELinux (security-enhanced Linux) gán label cho mọi object trên hệ thống, và số cổng TCP cũng là object như các object khác. SSH daemon chạy trong một domain bị giới hạn có tên là sshd_t. Policy cho phép sshd_t bind vào các cổng TCP được gắn label ssh_port_t. Theo mặc định, chỉ cổng 22 có label này. Khi yêu cầu daemon bind vào 2222, kernel kiểm tra label, thấy policy đã gán một generic type nào đó cho số cổng này, rồi từ chối quyền name_bind trên socket.
Đó là lý do lỗi này không giống lỗi firewall. Kernel từ chối trước khi listening socket được tạo, nên sshd ghi lỗi rồi thoát. Với lỗi firewall thì ngược lại: daemon vẫn đang chạy bình thường, nhưng packet bị loại bỏ trên đường đi vào.
getenforce cho biết máy đang ở mode nào. Ở Permissive, denial được ghi lại nhưng không bị enforce, nên thay đổi cổng có vẻ hoạt động, rồi hỏng vào ngày ai đó chạy setenforce 1 hoặc máy reboot vào enforcing mode. Dù ở mode nào, hãy gán label cho cổng. Hướng dẫn cơ bản về SELinux cho server giải thích đầy đủ về mode, context và boolean.
Bước 3: đặt port trong cấu hình sshd
Trên Rocky Linux 9 và 10, AlmaLinux 9 và 10 cũng như Fedora hiện tại, /etc/ssh/sshd_config bắt đầu bằng một dòng include, vì vậy vị trí gọn gàng để thay đổi là một file drop-in. Các bản cập nhật package sẽ không làm xung đột với thay đổi của bạn.
grep -n '^Include' /etc/ssh/sshd_config
echo 'Port 2222' | sudo tee /etc/ssh/sshd_config.d/10-port.conf
sudo sshd -tNếu grep không tìm thấy dòng Include, như trên Rocky Linux 8 và các image cũ hơn, hãy ghi Port 2222 trực tiếp vào /etc/ssh/sshd_config. sshd -t sẽ phân tích toàn bộ cấu hình, bao gồm cả drop-in, rồi báo lỗi cú pháp. Hãy sửa mọi lỗi được báo trước khi restart, vì cấu hình không phân tích được sẽ khiến daemon không khởi động lại.
Port có thể xuất hiện nhiều lần và sshd sẽ listen trên mọi port được liệt kê. Giữ Port 22 cùng với Port 2222 trong ngày đầu tiên là một biện pháp dự phòng đơn giản, miễn là bạn nhớ xóa nó.
sshd của bạn có được khởi động bằng socket unit không?
Một số image khởi động SSH bằng socket activation của systemd thay vì chạy dưới dạng service lâu dài. Khi được cấu hình như vậy, systemd quản lý listening socket và chuyển các kết nối cho sshd, nên dòng Port trong sshd_config bị bỏ qua hoàn toàn. Hãy kiểm tra trước khi restart bất kỳ thành phần nào:
systemctl is-enabled sshd.socketCâu trả lời enabled có nghĩa là port được cấu hình trong socket unit, không phải trong sshd_config:
sudo systemctl edit sshd.socket[Socket]
ListenStream=
ListenStream=2222ListenStream= không có giá trị là bắt buộc. Các giá trị được cộng dồn qua những drop-in, nên nếu không gán giá trị rỗng để xóa danh sách trước, socket vẫn listening trên cả 22 và 2222. Áp dụng thay đổi bằng sudo systemctl daemon-reload, sau đó chạy sudo systemctl restart sshd.socket. Nếu unit bị disable hoặc không tồn tại trên server của bạn, phần này không áp dụng.
Bước 4: khởi động lại, rồi kiểm tra từ một terminal thứ hai
sudo systemctl restart sshd
systemctl status sshd
sudo ss -tlnp | grep sshdGiữ terminal này mở. Không đăng xuất khỏi terminal. Mở một terminal thứ hai trên máy của bạn và kết nối qua cổng mới:
ssh -p 2222 youruser@203.0.113.10Chỉ đóng phiên đầu tiên sau khi đăng nhập từ terminal thứ hai thành công. Nếu đăng nhập không thành công, bạn vẫn còn một shell để hoàn tác mọi thay đổi. Thói quen này quyết định việc thay đổi chỉ mất 5 phút hay phải mất cả buổi chiều trên console của nhà cung cấp.
Bị firewall drop hay SELinux từ chối? Cách phân biệt
Từ laptop, hai lỗi này gần như giống hệt nhau. Trên server, chúng hoàn toàn khác nhau.
- Nếu
systemctl status sshdcho biết unit đã fail, daemon chưa lấy được socket. Đây là lỗi cấu hình hoặc SELinux từ chối. - Nếu unit đang active và
ss -tlnpcho biết sshd đã bind vào port mới, daemon không có lỗi. Vấn đề nằm trên đường truyền mạng: firewalld, firewall riêng của nhà cung cấp, hoặc địa chỉ và port bạn đã dùng để kết nối.
Với trường hợp SELinux, hãy đọc audit record thay vì đoán:
sudo ausearch -m AVC -ts recent
sudo journalctl -u sshd -n 50 --no-pagerMột lần từ chối name_bind trên class tcp_socket cho biết process trong comm="sshd", số port trong src= và label thực tế của port trong tcontext=. Trường cuối cùng là thông tin cần kiểm tra. Giá trị khác ssh_port_t nghĩa là bước 2 chưa áp dụng cho port bạn đang dùng, thường do nhập sai số port hoặc chọn sai protocol. Cài setroubleshoot-server nếu bạn muốn sealert chuyển record thành một câu mô tả.
Thông báo do chính sshd ghi khi kernel từ chối bind có dạng như sau:
error: Bind to port 2222 on 0.0.0.0 failed: Permission denied.Permission denied trên port lớn hơn 1024, nơi không cần quyền root để bind, là dấu hiệu đặc trưng của SELinux. Address already in use trong cùng dòng đó là một lỗi khác: process khác đang giữ port. Ở phía client, sự khác nhau giữa connection refused và connection timed out giúp phân biệt hai trường hợp mạng, vì connection refused nghĩa là packet đã đến host nhưng không có process nào listening, còn timeout nghĩa là không có phản hồi nào.
Đóng cổng 22 và cập nhật client
Sau khi đã đăng nhập thành công vài lần qua cổng mới, hãy gỡ cổng 22:
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --reload
sudo firewall-cmd --list-allGiữ nguyên SELinux label trên cổng 22. Label này đến từ policy cơ sở và không cấp quyền truy cập nào khi firewall không còn cho phép packet đi vào.
Sau đó sửa cấu hình trên các client, vì mọi công cụ mặc định dùng cổng này giờ cần được chỉ định lại. Khai báo một lần trong ~/.ssh/config trên máy của bạn thay vì phải nhập -p mãi:
Host myvps
HostName 203.0.113.10
Port 2222
User youruserscp, sftp, rsync và Ansible đều đọc file đó. Các job backup, monitoring check và cron script ghi cứng cổng 22 thì không đọc file này, nên hãy tìm và sửa chúng ngay khi thay đổi vẫn còn mới trong đầu.
Việc đổi cổng giúp gì và không giúp gì
Việc này giảm nhiễu trong log. Các scanner tự động liên tục quét cổng 22. Chuyển SSH sang cổng khác sẽ loại bỏ phần lớn các dòng đó khỏi journal, giúp bạn dễ thấy các sự kiện thực sự hơn. Đây không phải là biện pháp bảo mật. Bất kỳ scanner nào quét toàn bộ dải cổng cũng sẽ tìm thấy daemon của bạn và đọc được version banner của nó. Hãy xem việc đổi cổng là công việc dọn dẹp. Biện pháp bảo vệ thực sự là chỉ cho phép xác thực bằng key và tắt đăng nhập bằng password. Hướng dẫn hardening SSH cho VPS trình bày từng bước việc này.
Mọi nội dung trên hoạt động giống hệt nhau trên cả hai bản RHEL rebuild chính, vì chúng được build từ cùng một source. Nếu vẫn đang chọn giữa hai bản này, hãy xem So sánh Rocky Linux và AlmaLinux. Trước khi làm theo một hướng dẫn cũ, hãy kiểm tra chính xác release được cấp cho bạn bằng cat /etc/os-release. Các hướng dẫn viết cho Rocky Linux 8 vẫn thường xuất hiện ở vị trí cao trong kết quả tìm kiếm. Các bước semanage và firewall-cmd trong đó vẫn đúng. Tuy nhiên, Rocky 8 không có dòng include sshd_config.d và cũng không có socket unit cần xem xét. Vì vậy, phần sshd trong các hướng dẫn đó không phù hợp với một máy hiện tại.
fail2ban phải được cấu hình để biết cổng mới
fail2ban không có trong các repository cơ sở. Gói này được cung cấp qua EPEL (extra packages for enterprise Linux):
sudo dnf install -y epel-release
sudo dnf install -y fail2ban fail2ban-firewalldSubpackage fail2ban-firewalld khiến fail2ban ghi các lệnh ban thông qua firewalld. Đây là lựa chọn phù hợp trên máy chủ nơi firewalld quản lý ruleset.
Jail mặc định sshd đặt port = ssh. Tên này được phân giải qua /etc/services thành 22. Sau khi đổi cổng, jail sẽ theo dõi một cổng không có ai tấn công. Vì vậy nó không ban địa chỉ nào, trong khi các lần đăng nhập thất bại tiếp tục tăng trên 2222. Đặt cổng bằng số trong /etc/fail2ban/jail.local:
[sshd]
enabled = true
port = 2222
backend = systemd
maxretry = 5
bantime = 3600backend = systemd đọc các lần thất bại từ journal thay vì /var/log/secure. Đây là lựa chọn an toàn hơn trên bản cài tối giản, nơi rsyslog có thể không tồn tại. Khởi động bằng sudo systemctl enable --now fail2ban và kiểm tra jail bằng sudo fail2ban-client status sshd. Cú pháp jail giống với cấu hình được dùng trong thiết lập fail2ban cho SSH trên Ubuntu 24.04. Chỉ khác nguồn package và ban action.
Bản vá quan trọng hơn số cổng
Một server đã chuyển cổng SSH nhưng bốn tháng chưa cài các bản cập nhật bảo mật sẽ ở tình trạng tệ hơn server vẫn dùng cổng 22 nhưng tự động cài bản vá mỗi đêm. Hãy bật cập nhật không cần giám sát trong cùng phiên làm việc, khi bạn đã có quyền root: cập nhật dnf tự động trên Rocky Linux và AlmaLinux hướng dẫn cấu hình timer và chọn giữa tải bản cập nhật xuống hoặc cài đặt chúng.
FAQ
Vì sao sshd không khởi động sau khi tôi đổi cổng trên Rocky Linux?
Gần như luôn là do thiếu nhãn cổng SELinux. sshd chạy trong domain sshd_t bị giới hạn, và policy chỉ cho phép nó bind vào các cổng có nhãn ssh_port_t; mặc định chỉ có cổng 22. Kernel từ chối thao tác bind, nên daemon thoát thay vì lắng nghe, và journalctl -u sshd chứa một dòng có dạng error: Bind to port 2222 on 0.0.0.0 failed: Permission denied.. Chạy sudo semanage port -a -t ssh_port_t -p tcp 2222 với số cổng của bạn, rồi khởi động lại service. Nếu không tìm thấy semanage, hãy cài policycoreutils-python-utils trước.
Tôi vẫn cần semanage khi SELinux đang ở chế độ permissive không?
Có. Ở chế độ permissive, denial được ghi lại nhưng thao tác bind vẫn được cho phép, nên thay đổi có vẻ đã hoạt động. Tuy nhiên, nhãn vẫn bị thiếu. Ngay khi ai đó chạy setenforce 1, hoặc máy khởi động với SELINUX=enforcing trong /etc/selinux/config, sshd sẽ không khởi động trên cổng đó. Thêm nhãn chỉ cần một lệnh và loại bỏ một lỗi nếu không sẽ xuất hiện sau nhiều tuần mà không có nguyên nhân rõ ràng.
Cổng đã có nhãn và sshd đang chạy, vậy vì sao kết nối của tôi bị timeout?
Daemon đang chạy nghĩa là SELinux đã cho phép, nên packet đang bị drop trên đường đi vào. Kiểm tra sudo firewall-cmd --list-ports cho cổng của bạn, và xác nhận bạn đã chạy firewall-cmd --reload sau rule --permanent, vì chỉ có rule permanent thì firewall đang chạy sẽ không áp dụng rule đó. Sau đó kiểm tra control panel của nhà cung cấp host để tìm network firewall riêng ở phía trước VPS. Đây là lớp thứ hai thường chặn kết nối, và không có thành phần nào bên trong operating system hiển thị được nó.
Tôi nên dùng cổng nào thay cho 22?
Bất kỳ cổng TCP còn trống nào từ 1024 đến 65535. Tránh dùng 2222 và 22222 trên server thực tế, vì scanner sẽ thử các cổng này ngay sau cổng 22. Xác nhận số cổng còn trống bằng sudo ss -tlnp, xác nhận policy của SELinux chưa chiếm cổng đó bằng sudo semanage port -l, và tránh mọi cổng đã được gán cho service mà bạn có thể cài sau này. Một số cổng cao và khó nhớ vẫn được, vì bạn chỉ cần ghi nó vào ~/.ssh/config một lần và không phải nhập lại.