Secure Ubuntu server bằng VPN WireGuard: giới hạn SSH chỉ qua mạng riêng
SSH mở ra Internet → tiện nhưng rủi ro. Bot quét port 22 liên tục, brute-force, dò user, thử key yếu, khai thác CVE mới. Dù bạn dùng key auth, tắt password, đổi port SSH, bật Fail2ban… bề mặt tấn công vẫn còn.
Giải pháp mạnh hơn: không public SSH. Chỉ cho SSH chạy qua mạng riêng WireGuard VPN. Internet không thấy SSH. Muốn quản trị server → phải kết nối VPN trước.
Mô hình:
– Server Ubuntu public IP.
– WireGuard tạo interface riêng: wg0.
– Server VPN IP: 10.8.0.1.
– Laptop/admin VPN IP: 10.8.0.2.
– SSH chỉ listen trên 10.8.0.1 hoặc firewall chỉ cho SSH từ 10.8.0.0/24.
– Port WireGuard UDP 51820 mở ra Internet.
– Port SSH 22 đóng với Internet.
Kết quả: attacker scan public IP → không thấy SSH. Admin có VPN key → vào được.
Vì sao nên đặt SSH sau WireGuard?
Giảm bề mặt tấn công
SSH public → ai cũng thử kết nối được. Dù không login được, daemon vẫn nhận traffic.
SSH qua WireGuard → chỉ peer có private key hợp lệ mới vào mạng VPN. Không có key → packet bị bỏ.
Public SSH → bị quét. SSH private → gần như “biến mất”.
Bảo mật nhiều lớp
Bạn có 2 lớp:
1. WireGuard key để vào mạng riêng.
2. SSH key để đăng nhập server.
Mất 1 key chưa đủ. Attacker cần cả VPN private key + SSH private key + user hợp lệ.
Quản trị sạch hơn
Có nhiều server? Gán mỗi server 1 IP VPN:
– 10.8.0.1 → bastion/server chính
– 10.8.0.10 → app server
– 10.8.0.20 → DB server
– 1 Ubuntu server có public IP.
– Quyền root hoặc user sudo.
– 1 client: laptop Linux/macOS/Windows.
– Đã có SSH hiện tại để cấu hình ban đầu.
Cảnh báo: Đừng khóa SSH public trước khi xác nhận WireGuard hoạt động. Luôn giữ 1 session SSH hiện tại mở trong lúc test.
Cài WireGuard trên Ubuntu server
Cập nhật pkg:
sudo apt update
sudo apt install -y wireguard
Tạo key:
wg genkey | sudo tee /etc/wireguard/server_private.key | wg pubkey | sudo tee /etc/wireguard/server_public.key
sudo chmod 600 /etc/wireguard/server_private.key
– Address → IP VPN client.
– Endpoint → public IP server + port WireGuard.
– AllowedIPs = 10.8.0.0/24 → chỉ route mạng VPN qua tunnel, không route toàn bộ Internet.
– PersistentKeepalive = 25 → hữu ích nếu client sau NAT.
– Kiểm tra sudo wg trên server có handshake chưa.
– Kiểm tra public IP/port đúng chưa.
– Kiểm tra UDP 51820 được mở chưa.
– Kiểm tra key peer đúng chưa.
– Kiểm tra AllowedIPs không trùng sai.
Cách 1: Giới hạn SSH bằng firewall UFW
Đây là cách dễ, ít rủi ro. SSH vẫn listen mọi interface, nhưng firewall chỉ cho VPN subnet vào.
Cho phép SSH từ VPN:
sudo ufw allow from 10.8.0.0/24 to any port 22 proto tcp
Từ chối SSH public:
sudo ufw deny 22/tcp
Bảo đảm WireGuard vẫn mở:
sudo ufw allow 51820/udp
Bật UFW nếu chưa bật:
sudo ufw enable
sudo ufw status verbose
Thứ tự rule UFW có thể ảnh hưởng. Kiểm tra:
sudo ufw status numbered
Bạn muốn thấy logic:
– Allow 51820/udp từ anywhere.
– Allow 22/tcp từ 10.8.0.0/24.
– Deny 22/tcp từ anywhere.
Kỳ vọng: qua VPN OK, public IP bị timeout/refused.
Cách 2: Cho SSH chỉ listen trên IP WireGuard
Cách này gọn hơn: SSH daemon chỉ bind vào 10.8.0.1, không bind public IP.
Mở file:
sudo nano /etc/ssh/sshd_config
Thêm/sửa:
ListenAddress 10.8.0.1
Kiểm tra config:
sudo sshd -t
Restart SSH:
sudo systemctl restart ssh
Kiểm tra socket:
ss -tlnp | grep ssh
Kỳ vọng:
LISTEN 0 128 10.8.0.1:22 ...
Không nên thấy:
0.0.0.0:22
Lưu ý quan trọng: Nếu SSH khởi động trước WireGuard, IP 10.8.0.1 chưa tồn tại → SSH có thể fail bind. Khắc phục bằng systemd dependency hoặc dùng firewall thay vì bind IP. Với hầu hết case, UFW allow/deny đơn giản hơn, an toàn hơn.
Hardening SSH bổ sung
Dù SSH đã nằm sau VPN, vẫn nên harden.
Mở:
sudo nano /etc/ssh/sshd_config
Khuyến nghị:
PasswordAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
KbdInteractiveAuthentication no
X11Forwarding no
AllowUsers youruser
Test config:
sudo sshd -t
sudo systemctl reload ssh
Ý nghĩa:
– PasswordAuthentication no → chặn brute-force password.
– PermitRootLogin no → tránh login root trực tiếp.
– AllowUsers → chỉ user cụ thể được SSH.
– reload thay vì restart → ít gián đoạn hơn.
Quản lý nhiều admin
Mỗi admin nên có peer riêng. Không dùng chung key.
– Thu hồi 1 người → xóa peer tương ứng.
– Audit dễ hơn.
– Không phải rotate toàn bộ VPN.
Thu hồi admin:
1. Xóa block [Peer].
2. Restart WireGuard.
3. Xóa SSH public key của admin khỏi ~/.ssh/authorized_keys nếu cần.
Logging và kiểm tra định kỳ
Kiểm tra WireGuard:
sudo wg show
Xem peer, latest handshake, transfer.
Kiểm tra SSH login:
sudo journalctl -u ssh --since "1 day ago"
Kiểm tra auth log:
sudo grep sshd /var/log/auth.log
Kiểm tra port public từ máy ngoài:
nmap -Pn -p 22,51820 SERVER_PUBLIC_IP
Kỳ vọng:
– 22/tcp → filtered/closed.
– 51820/udp → open|filtered là bình thường với UDP.
Lỗi thường gặp
Có handshake nhưng không SSH được
Nguyên nhân hay gặp:
– SSH không listen trên 10.8.0.1.
– UFW chặn subnet VPN.
– Client route sai.
– User/key SSH sai.
Debug:
ping 10.8.0.1
nc -vz 10.8.0.1 22
sudo ufw status verbose
ss -tlnp | grep ssh
Không có handshake
Kiểm tra:
– Endpoint đúng public IP?
– UDP 51820 mở ở UFW + cloud firewall?
– Server ListenPort đúng?
– Key public/private khớp?
– Client bật tunnel chưa?
Lệnh:
sudo wg
sudo journalctl -u wg-quick@wg0
Tự khóa khỏi server
Nếu lỡ chặn SSH public trước khi VPN hoạt động:
– Dùng cloud console/VNC/serial console.
– Sửa UFW/sshd_config.
– Mở lại SSH tạm:
sudo ufw allow 22/tcp
sudo systemctl restart ssh
Kết luận thực tế
SSH public không còn là mặc định tốt cho server quan trọng. Cấu hình an toàn hơn: WireGuard public, SSH private.
Checklist cuối:
– WireGuard chạy trên wg0.
– Client ping được 10.8.0.1.
– SSH qua 10.8.0.1 OK.
– UFW chỉ allow SSH từ 10.8.0.0/24.
– Public port 22 bị chặn.
– SSH tắt password, tắt root login.
– Mỗi admin có VPN peer riêng.
Mô hình này đơn giản, rẻ, nhanh, hiệu quả. Một port UDP WireGuard thay cho SSH public → bề mặt tấn công giảm mạnh. Với vài dòng config, Ubuntu server kín hơn rất nhiều nhưng vẫn dễ quản trị hằng ngày.