보안 그룹과 네트워크 ACL, 상태를 기억하느냐가 가른다
요청은 통과했는데 응답이 막힐 수도 있어요. 보안 그룹과 네트워크 ACL이 연결 상태를 다루는 차이를 따라가 봐요.
이 글의 목차허용만 있고 거부가 없다4
건물 출입을 관리하는 방식에는 두 가지가 있어요. 나간 사람이 돌아오는 것을 기억해 두고 그냥 들여보내는 방식과, 나갈 때와 들어올 때를 각각 따로 허가받게 하는 방식이에요. 앞의 것은 편하고 뒤의 것은 엄격해요. 문제는 뒤의 방식을 앞의 방식이라고 생각하고 규칙을 쓸 때 생겨요.
앞 시리즈의 포트와 방화벽 편에서 상태를 기억하는 방화벽을 다뤘어요. AWS에는 기억하는 것과 기억하지 않는 것이 둘 다 있고, 이 차이를 모르면 규칙을 아무리 정확히 써도 통신이 되지 않아요.
허용만 있고 거부가 없다
보안 그룹에는 허용 규칙만 쓸 수 있어요. 무엇을 막을지 적는 칸이 아예 없고, 적히지 않은 것은 전부 막혀요. 앞 시리즈에서 다룬 기본값을 거부로 두는 원칙이 도구 수준에서 강제된 형태예요. 목록에서 무언가를 빠뜨리면 그것은 열린 채 남는 것이 아니라 닫힌 채 남아요.
이 성질 덕분에 보안 그룹은 실수가 사고로 이어지기 어려워요. 대신 반대 방향의 불편이 있어요. 특정 대상만 막고 싶어도 그렇게 쓸 수 없어서, 나머지 전부를 허용 목록으로 적어야 해요. 그런 요구가 있을 때 쓰는 것이 다음 이야기예요.
상태를 기억하는 쪽과 아닌 쪽
서브넷은 네트워크 주소 공간을 나눈 구획이에요. 그 구획의 경계에서 쓰는 네트워크 ACL(Access Control List)은 접근 허용·거부 목록이에요. 보안 그룹과 달리 거부 규칙도 있고, 규칙 번호 순서대로 확인해 처음 일치한 동작을 적용해요. 그리고 연결 상태를 기억하지 않아요.
AWS 문서는 보안 그룹이 상태를 저장한다고 명시해요. 인스턴스에서 요청을 보내면 그 응답은 인바운드 규칙과 무관하게 들어올 수 있고, 허용된 인바운드 트래픽에 대한 응답도 아웃바운드 규칙과 무관하게 나갈 수 있어요. 반대로 네트워크 ACL은 상태를 저장하지 않으므로, 인바운드 트래픽에 대한 응답을 허용하는 규칙을 따로 넣어야 해요.
예를 들어 웹 요청이 들어오는 방향은 ACL을 통과했지만 응답 방향이 막혔다고 해 볼게요. 클라이언트는 기다리다 시간 초과를 겪을 수 있어요. 요청이 어디까지 도착했는지에 따라 서버 기록은 남을 수도 있어요. ‘응답이 없다’는 증상만으로 요청이 들어오지 않았다고 단정하지 말고 양방향을 나눠 봐야 해요.
응답이 임시 포트로 돌아온다는 사실도 여기서 걸려요. 앞 시리즈에서 본 대로 클라이언트가 빌려 쓰는 번호는 그때그때 달라지므로, 상태를 기억하지 않는 쪽에서는 그 범위 전체를 열어 두어야 해요. 엄격하게 쓰려다 오히려 넓게 여는 상황이 생겨요.
허용된 요청의 응답이 어느 규칙을 따르는지 비교해 보세요.
- 요청규칙으로 허용
- 서버응답 생성
- 응답연결 상태로 허용
- 요청규칙으로 허용
- 서버응답 생성
- 응답NACL에서 차단
- 요청규칙으로 허용
- 서버응답 생성
- 응답별도 규칙으로 허용
상태를 기억하는 보안 그룹은 허용된 요청의 응답을 허용해요. NACL은 응답 방향에도 맞는 규칙이 있어야 해요.
보안 그룹은 허용된 연결을 기억해요. 그 응답은 반대 방향 규칙과 관계없이 허용돼요.
실제 연결 없이 한 연결의 왕복만 비교해요. 경로·리스너·다른 정책은 정상이며, 응답은 외부에서 먼저 거는 새 연결과 달라요.
주소 대신 그룹을 가리키기
보안 그룹의 규칙에는 허용할 출발지를 적는데, 여기에 주소 대역 말고 다른 보안 그룹을 적을 수 있어요.
차이는 시간이 지나면서 드러나요. 주소로 적어 두면 그 서버가 교체되거나 대수가 늘 때마다 규칙을 고쳐야 하고, 고치는 것을 잊으면 새로 뜬 서버만 통신이 되지 않아요. 앞 시리즈에서 본 대로 사설 주소는 자주 바뀌는 값이에요. 반면 그룹으로 적으면 규칙이 말하는 것이 주소가 아니라 역할이 돼요. 이 데이터베이스는 그 애플리케이션 그룹에 속한 것들에게만 열려 있다는 문장이 되고, 구성원이 바뀌어도 그 문장은 그대로예요.
| 어떻게 쓰나 | 무엇을 표현하나 | 무너지는 순간 |
|---|---|---|
| 출발지를 주소로 적는다 | 지금 그 자리에 있는 기계 | 교체나 증설 때 규칙이 어긋난다 |
| 출발지를 그룹으로 적는다 | 그 역할을 맡은 것들 | 그룹 배정을 빠뜨리면 조용히 막힌다 |
| 서브넷 단위로 막는다 | 그 구획 전체 | 응답용 규칙을 빠뜨리면 통신이 끊긴다 |
세 줄의 실패가 서로 다르게 나타난다는 점이 중요해요. 첫 줄은 새로 뜬 것만 안 되고, 둘째 줄은 배정을 잊은 것만 안 되며, 셋째 줄은 요청은 들어오는데 답이 안 나가요. 증상만 보고 원인을 짐작하기 어려운 이유예요.
저는 무엇으로 적었나
제가 만드는 앱에서 서비스끼리 부르는 통신은 전부 그룹 기준으로 적었어요. 처음에는 주소로 적는 편이 눈에 보여서 편했는데, 기계를 한 번 교체하고 나서 바꿨어요. 규칙을 고치는 일 자체는 몇 분이지만, 고쳐야 한다는 사실을 기억하는 일이 문제였어요. 그 기억은 반드시 급한 날에 빠져요.
서브넷 단위 규칙은 쓰지 않았어요. 필요할 만큼 복잡하지 않기도 했고, 응답 방향을 따로 챙겨야 하는 도구를 소규모 구성에 들이면 얻는 것보다 잃는 것이 크다고 봤어요. 막을 수 있는 것이 늘어나는 대신 잘못 막을 수 있는 것도 함께 늘어나요.
돌아보면 이 영역에서 오래 버티는 규칙은 촘촘한 규칙이 아니라 덜 자주 고쳐도 되는 규칙이었어요. 주소는 바뀌고 역할은 잘 바뀌지 않아요.