서비스를 두 대로 나누면서 저장소를 다른 장비로 뺐다. 붙질 않았다.
Table of contents
Open Table of contents
증상 — 붙질 않았다
돌아온 것은 이 한 줄이었다.
Connection refused
Connection refused 를 보고 방화벽을 먼저 의심했는데 그쪽은 열려 있었다.
전에는 같은 장비 안에 있었으니 아무 문제가 없었고 장비를 나누면서 무엇이 달라졌는지를 찾아야 했다.
원인 — 어디에 붙어 있는지 봤다
ss 로 듣고 있는 주소를 봤다.
$ ss -lntp | grep 6379
LISTEN 0 128 127.0.0.1:6379 0.0.0.0:* users:(("redis-server",pid=1841,...))
127.0.0.1:6379 라 그 장비 안에서만 붙는다.
방화벽을 다 열어도 밖에서는 못 붙고 0.0.0.0:6379 였다면 모든 주소에서 받았을 텐데 지금은 아니었다.
같은 장비 안에 있을 때는 127.0.0.1 로 충분했고 장비를 나누는 작업에 bind 를 다시 정하는 일이 딸려 있다는 것을 몰랐다.
비교 — 거부와 시간 초과
Connection refused 라는 메시지가 이미 답을 가리키고 있었다.
Connection refused → 아무도 안 듣고 있다
Connection timed out → 듣고는 있는데 중간에 막혔다
refused 는 그 주소와 포트에 닿았는데 받는 쪽이 없다는 뜻이다.
방화벽이 DROP 하면 대개 응답 없이 timed out 이 되고 두 신호를 구분하지 않으면 방화벽만 계속 뒤지게 된다.
메시지가 어느 쪽인지 먼저 봤어야 했다. 그 한 줄이 조사 방향을 정하고 있었다.
조치 — 필요한 주소만 열기
전부 여는 대신 필요한 주소만 적었다.
bind 127.0.0.1 10.0.0.31
bind 에 적은 두 주소만 듣고 0.0.0.0 으로 두면 공인 주소에서도 받는다.
주소를 지정하면 그 주소가 장비에 붙어 있어야 뜬다.
# 주소가 아직 안 붙었을 때
Warning: Could not create server TCP listening socket 10.0.0.31:6379: bind: Cannot assign requested address
부팅 순서에 걸리므로 network-online.target 뒤에 뜨게 했다.
# /etc/systemd/system/redis.service.d/override.conf
[Unit]
After=network-online.target
Wants=network-online.target
좁게 여는 대신 뜨는 조건이 하나 늘어난 셈이다.
설정 — 인증과 위험한 명령
로컬에서만 듣던 동안은 인증이 없었다.
requirepass <값>
밖에서 붙게 되면 그 제약이 사라지므로 requirepass 를 켰다.
$ chmod 600 /etc/redis/redis.conf
$ chown redis:redis /etc/redis/redis.conf
값을 설정 파일에 두고 파일 권한을 좁혔으며 애플리케이션 쪽은 코드가 아니라 환경에서 읽게 했다.
붙을 수 있게 되면 붙어서 할 수 있는 것도 는다.
rename-command FLUSHALL ""
rename-command CONFIG ""
rename-command 로 이름을 비워 운영에서 쓸 일 없는 것을 없앴다.
CONFIG 를 막으면 운영 중에 설정을 못 바꾼다. 그것이 불편한지 위험한지를 재고 설정은 파일로 바꾸고 재시작하기로 정했다.
검증 — 순서와 양쪽 확인
듣는 주소를 정한 뒤에 들어올 곳을 정했다.
$ iptables -A INPUT -p tcp --dport 6379 -s 10.0.0.32 -j ACCEPT
$ iptables -A INPUT -p tcp --dport 6379 -j DROP
10.0.0.32 에서만 받고 나머지는 끊는다.
순서가 있다. 듣는 주소를 좁히는 것이 먼저고 방화벽이 나중이라 반대로 하면 방화벽 규칙 하나가 잘못됐을 때 그대로 열린다.
확인은 양쪽에서 했다.
# 애플리케이션 서버에서
$ nc -vz -w 3 10.0.0.31 6379
Connection to 10.0.0.31 6379 port [tcp/*] succeeded!
# 다른 장비에서
$ nc -vz -w 3 10.0.0.31 6379
nc: connect to 10.0.0.31 port 6379 (tcp) failed: Connection timed out
nc 로 보니 붙어야 할 곳에서 붙고 안 붙어야 할 곳에서 안 붙는다.
앞만 보면 열려 있으면 안 되는 경로가 열린 것을 못 본다.
$ redis-cli -h 10.0.0.31 ping
(error) NOAUTH Authentication required.
인증이 실제로 걸렸는지도 같은 방식으로 확인했다.
정리
- 로컬 주소에만 붙어 있으면 방화벽을 다 열어도 밖에서 못 붙는다
- 거부와 시간 초과는 다른 신호다
- 거부는 아무도 안 듣는 것이고 시간 초과는 중간에 막힌 것이다
- 그 한 줄이 조사 방향을 정한다
- 장비를 나누면 듣는 주소를 다시 정해야 한다
- 전부 여는 대신 필요한 주소만 지정한다
- 주소를 지정하면 그 주소가 붙은 뒤에 떠야 한다
- 밖에서 붙게 되면 인증을 켜고 값은 환경에서 읽는다
- 운영에서 쓸 일 없는 명령은 없애되 불편한지 위험한지를 잰다
- 듣는 주소를 좁히고 나서 방화벽을 건다
- 붙어야 할 곳과 안 붙어야 할 곳을 둘 다 확인한다