# Connection refused mà không phải sshd: chuyện shared NAT + fail2ban

> Hai thiết bị cùng văn phòng share 1 public IP qua NAT. Một máy bàn brute force SSH fail → fail2ban ban IP → Mac tôi cũng mất SSH dù không làm gì. Diagnose pipeline + whitelist permanent.

**Author**: Tien Dang (Đặng Hồng Tiên), Founder of OKG and AIC, Vietnam
**Published**: 2026-05-12
**Pillar**: ops
**Tags**: vps, ssh, fail2ban, nat, debugging, ubuntu-pro
**Canonical URL**: https://danghongtien.com/posts/2026-05-12-connection-refused-shared-nat-fail2ban/
**AI assistance disclosed**: yes (structure draft)

---

## TL;DR
VPS production refuse SSH từ Mac. Diagnose nhầm sang sshd / UFW. Root cause thực tế: Mac và máy bàn cùng văn phòng share 1 public IP qua NAT. Một máy thử login user sai 5 lần → fail2ban ban IP → Mac cũng dính. Fix permanent: whitelist IP văn phòng trong jail.d/whitelist.conf.

## Key claims
- Hai thiết bị cùng văn phòng share 1 public IP qua NAT — fail2ban ban IP đó nghĩa là cả nhóm cùng mất SSH cùng lúc
- 'Connection refused' ở TCP port 22 thường là REJECT từ iptables (fail2ban) chứ không phải sshd down
- Diagnose đúng: chạy fail2ban-client status sshd TRƯỚC khi nghi sshd hỏng / UFW chặn / cloud firewall block
- ignoreip trong /etc/fail2ban/jail.d/whitelist.conf là fix permanent, không cần restart sshd
- SSH key auth + tắt PasswordAuthentication là giải pháp gốc — triệt tiêu trigger brute force ngay từ đầu

import { Image } from 'astro:assets';
import natTopology from '../../assets/posts/2026-05-12-connection-refused-shared-nat-fail2ban/01-nat-shared-fail2ban.svg';

## TL;DR

VPS production của tôi tự dưng refuse SSH từ Mac. Diagnose nhầm sang sshd hỏng → UFW chặn → cloud firewall block. Hóa ra Mac và máy bàn Ubuntu cùng văn phòng share 1 public IP qua NAT. Lúc tôi thử SSH từ máy bàn với username sai 5 lần, fail2ban ban public IP đó. Mac chia sẻ cùng IP nên cũng dính. Fix permanent là `ignoreip` trong fail2ban jail config — không cần touch sshd.

## 1. Bối cảnh

Tôi attach Ubuntu Pro cho VPS production xong, chuyển sang chạy `apt update`. SSH từ Mac:

```
ssh root@$VPS_HOST
ssh: connect to host xxx.xxx.xxx.32 port 22: Connection refused
```

Lạ. VPS reachable qua ping. Console của nhà cung cấp vào được — sshd đang `Active: active (running)`. Port 22 lắng nghe đúng. Vậy tại sao "Connection refused"?

## 2. Diagnose pipeline — cái sai trước khi đúng

### Hypothesis 1: sshd hỏng

Trên console, `systemctl status ssh` → active running, PID rõ ràng, `ss -tlnp | grep :22` → `LISTEN 0.0.0.0:22`. Loại.

### Hypothesis 2: UFW chặn

```
ufw status verbose
22/tcp                     ALLOW IN    Anywhere
22/tcp (v6)                ALLOW IN    Anywhere (v6)
```

Allow rộng rãi. Loại.

### Hypothesis 3: fail2ban ban

```
fail2ban-client status sshd
|- Currently banned: 0
|- Total banned: 132
`- Banned IP list:
```

Currently banned = 0. Tôi loại fail2ban quá nhanh. **Sai lầm số một**: total banned 132 lifetime nghĩa là fail2ban active sẵn — phải hỏi tiếp "vừa rồi 5 phút có IP nào bị ban hết hạn ngay trước khi check không?".

### Hypothesis 4: Cloud provider firewall

Tôi định check Hetzner cloud panel. Trước đó test lại 1 lần nữa:

```
ssh -o ConnectTimeout=5 root@$VPS_HOST "hostname && date"
back-online-host
Tue May 12 18:27:51 +07 2026
```

**SSH bỗng dưng OK trở lại.** Không touch gì cả.

## 3. Root cause — shared NAT

Tôi check IP public của Mac:

```
curl -4 ifconfig.me
aa.bb.cc.dd
```

Sau đó hỏi anh đồng nghiệp ngồi cạnh bàn — máy Ubuntu của anh ấy:

```
curl -4 ifconfig.me
aa.bb.cc.dd
```

**Cùng IP.** Cả văn phòng share 1 public IP qua NAT router. Vài phút trước, anh đồng nghiệp thử SSH vào VPS với username `tien` (sai — server chỉ có user `root`). 5 lần fail liên tiếp.

Log sshd trên VPS xác nhận:

```
sshd[902847]: Failed password for invalid user tien from aa.bb.cc.dd port 44346
sshd[902847]: pam_unix(sshd:auth): check pass; user unknown
sshd[902847]: Failed password for invalid user tien from aa.bb.cc.dd port 44346
sshd[902847]: PAM 2 more authentication failures
sshd[902963]: Invalid user tien from aa.bb.cc.dd port 59200
```

fail2ban bắt pattern, ban IP `aa.bb.cc.dd` 10 phút mặc định. Trong khoảng đó:

- Máy bàn → "Connection refused" (vì nó là thủ phạm)
- Mac của tôi → "Connection refused" (vì share IP)
- Console nhà cung cấp → vẫn vào được (route qua link-local `169.254.0.1`, không qua public Internet)

Khi tôi test lại lúc 18:27, ban đã hết hạn → SSH OK lại.

<figure>
  <Image src={natTopology} alt="Sơ đồ topology: Mac và máy bàn đồng nghiệp cùng văn phòng → cùng đi qua NAT router → public IP aa.bb.cc.dd → VPS. Khi máy bàn brute force fail nhiều lần, fail2ban ban IP aa.bb.cc.dd ở iptables → CẢ HAI thiết bị mất SSH. Console nhà cung cấp dùng link-local 169.254.0.1 bypass internet, vẫn vào được sau ban." loading="lazy" />
  <figcaption>Topology shared NAT: 2 thiết bị cùng public IP. fail2ban ban "thủ phạm" cũng đồng nghĩa ban luôn neighbor — collateral damage cho team share NAT.</figcaption>
</figure>

## 4. Fix permanent — whitelist trong fail2ban

Tạo file `/etc/fail2ban/jail.d/whitelist.conf`:

```ini
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 aa.bb.cc.dd
```

Restart:

```bash
systemctl restart fail2ban
fail2ban-client get sshd ignoreip
# These IP addresses/networks are ignored:
# |- 127.0.0.0/8
# |- ::1
# `- aa.bb.cc.dd
```

Từ giờ máy bàn anh đồng nghiệp có brute force fail bao nhiêu lần, fail2ban cũng sẽ skip không ban. Mac tôi không còn dính vạ.

**Không cần `systemctl restart sshd`.** sshd hoàn toàn không biết fail2ban tồn tại — fail2ban chỉ chèn rule iptables ở chain `f2b-sshd`, không can thiệp sshd daemon.

## 5. Giải pháp gốc hơn — SSH key + tắt password auth

Whitelist là band-aid. Vấn đề thực sự: **brute force fail xảy ra vì có cơ chế password auth để mà brute force**. Triệt nó:

Trên máy đồng nghiệp:

```bash
ssh-keygen -t ed25519 -C "office-ubuntu"
ssh-copy-id root@$VPS_HOST   # nhập password lần cuối
ssh root@$VPS_HOST           # từ giờ không cần password
```

Trên VPS, sửa `/etc/ssh/sshd_config`:

```
PasswordAuthentication no
PubkeyAuthentication yes
```

Reload sshd:

```bash
systemctl reload sshd
```

Bot brute force từ Internet vẫn sẽ probe port 22 hàng ngày, nhưng tất cả sẽ rớt ở `auth methods exhausted` chứ không tới `Failed password`. fail2ban jail sshd default chỉ trigger trên `Failed password` regex — không có pattern → không có ban → không có collateral damage cho team share NAT.

## 6. Reservations

**Whitelist không phải silver bullet.**

- ISP cấp DHCP cho văn phòng đổi IP sau vài ngày → whitelist hết tác dụng. Phải dùng dynamic DNS + script update jail.d hoặc đẩy luôn lên SSH key.
- Văn phòng share fiber với building bên cạnh → IP whitelist có thể bao gồm cả công ty khác. Risk nhỏ nhưng có.
- Whitelist subnet `/24` thay vì single IP an toàn hơn cho NAT pool, nhưng giảm security guard.

**fail2ban không bảo vệ DDoS volumetric.** Nó là tool chống brute force theo log pattern, không phải tool chống flood. Cloud-provider firewall + rate limit ở edge mới chống được flood.

**Cloud-provider firewall đôi khi cũng ban port 22 từ public.** Lúc đầu tôi nghĩ tới hypothesis này — không phải vô lý. Cần check panel provider trước khi dồn nghi vào fail2ban nếu fail2ban-client báo no current ban.

## 7. Bài học

Pipeline diagnose tôi sẽ dùng next time khi gặp "SSH Connection refused":

1. **Network reachable?** `ping <ip>` — nếu fail, network/routing.
2. **TCP port 22 mở?** `nc -zv <ip> 22` — refused vs timeout có ý nghĩa khác nhau (refused = REJECT/no listener, timeout = DROP/filtered).
3. **`fail2ban-client status sshd`** check current + log gần nhất — TRƯỚC khi nghi sshd/UFW.
4. **So sánh IP public của thiết bị mình với log fail trên server** — nếu cùng IP, share NAT là culprit số 1.
5. **Console nhà cung cấp** chỉ dùng để fix, không dùng để verify "SSH OK" — vì nó route qua link-local, không qua public Internet, không phản ánh trải nghiệm thật của client.

Tôi tốn 25 phút để khoanh vùng đúng. Lần sau pipeline trên sẽ cắt còn ~5 phút.

## Notes

Bài này là 1 episode trong chuỗi VPS ops của tôi. Bối cảnh rộng hơn: tôi đang vận hành 1 VPS production cho personal AI agent (JARVIS), với stack gồm ChromaDB container, n8n container, Langfuse container, blog static site (Astro), và 1 MCP server. Khi 1 dependency hạ tầng (như SSH access) lag, toàn bộ workflow chair-pop-up bị nghẽn. Đó là lý do tôi care về diagnose pipeline thay vì "thử restart đại".

## FAQ
### Tại sao máy của tôi mất SSH dù tôi không brute force?
Public IP qua NAT là chung cả văn phòng. Bất kỳ máy nào trong văn phòng login fail nhiều lần, fail2ban thấy IP nguồn đó và ban hết — không phân biệt thiết bị nào trigger.

### Làm sao biết IP đã bị fail2ban ban?
fail2ban-client status sshd hiển thị banned IP list. Currently banned = đang active block, Total banned = lifetime count. Lifetime cao không có nghĩa hiện tại đang bị ban.

### Whitelist IP văn phòng có rủi ro gì?
Phụ thuộc IP cố định. Nếu ISP cấp DHCP đổi IP sau vài ngày, whitelist hết hiệu lực. Văn phòng share fiber với building khác cũng có thể leak whitelist sang IP không mong muốn.

### Có cần restart sshd sau khi sửa fail2ban không?
Không. systemctl restart fail2ban đủ. sshd chỉ tương tác với fail2ban qua iptables rule chứ không phải qua signal hoặc reload — hai daemon độc lập.

### Livepatch Ubuntu Pro có thay được reboot không?
Patch security cho kernel hiện tại đang chạy = có. Boot vào kernel mới khi linux-image update = vẫn cần reboot. Livepatch giúp giảm tần suất reboot, không bỏ hoàn toàn.

---

Source: https://danghongtien.com/posts/2026-05-12-connection-refused-shared-nat-fail2ban/
Markdown export of canonical HTML article. License: CC BY 4.0 with attribution.
