트러블슈팅 9. 호스트에서는 되는데 컨테이너에서만 연결이 시간 초과될 때: mailcow 의 Fail2ban 이 상대 서버를 막고 있었다
고객이 원격 지원을 요청하면 우리 API 가 사내 채팅(Zulip)으로 알림을 보냅니다. 어느 날 아침 7시 52분부터 이 알림 API 가 계속 502 를 내고 있었습니다.
로그를 보니 API 컨테이너에서 채팅 서버로 가는 연결이 시간 초과였습니다. 그런데 같은 노드의 호스트 셸에서 curl 로 채팅 서버를 부르면 멀쩡히 응답이 왔습니다. 호스트는 되고 컨테이너만 안 되는 상황이었습니다.
이 노드에는 메일 서버(mailcow)도 같이 돌고 있습니다. mailcow 에는 로그인 실패가 반복되는 IP 를 차단하는 장치(netfilter 컨테이너, 관리 화면에서는 Fail2ban 설정)가 있는데, 혹시나 해서 방화벽 규칙을 뒤져 보니 채팅 서버 IP 가 들어 있었습니다.
sudo iptables-save | grep <채팅 서버 IP>
채팅 서버는 알림 메일을 보내려고 mailcow 의 SMTP 에 로그인하는데, 그 계정 비밀번호가 맞지 않아 몇 초마다 실패하고 있었습니다. mailcow 는 그걸 공격으로 보고 채팅 서버 IP 를 30분, 60분, 120분씩 늘려 가며 막았고요. 그 차단 규칙이 채팅 서버에서 오는 패킷을 버리니, 컨테이너로 돌아와야 할 채팅 서버의 응답까지 버려진 겁니다. 호스트에서 시험한 연결은 이 규칙에 걸리지 않아서 멀쩡해 보였습니다.
당장은 채팅 서버 IP 를 mailcow 차단 허용 목록에 넣고 걸려 있던 차단을 풀었습니다. 관리 화면의 Fail2ban 설정에 있는 허용 목록과 같은 곳입니다. 이제 로그인에 실패해도 “allowlisted” 로 남을 뿐 막히지 않습니다.
확인은 두 가지로 했습니다.
sudo docker logs --since 1h <알림 API 컨테이너> | grep notify # 200 이어야 함
sudo iptables-save | grep <채팅 서버 IP> # 아무것도 안 나와야 함
허용 목록은 증상을 없앤 것뿐이고, 원인은 채팅 서버에 저장된 SMTP 비밀번호가 메일 계정 비밀번호와 다르다는 겁니다. 이건 서버를 옮기기 전부터 그랬던 것이었는데, 메일 서버를 같은 노드로 옮기면서 차단이 알림 API 에까지 번진 겁니다. 채팅 서버의 메일 비밀번호를 맞추기 전까지는 놓친 메시지 알림 같은 메일이 나가지 않고, mailcow 로그에는 인증 실패가 계속 쌓입니다.
한 노드에 여러 서비스를 올리다 보면 이런 식으로 서로 엮입니다. 메일 서버의 차단 장치가 전혀 다른 서비스의 연결을 막을 수 있다는 건 이번에 처음 알았습니다.