폐쇄망 현장 — 방산·국방 도메인 · 나가는 통신이 없다는 것을 자료로 보이기 · 이론
밖으로 나가지 않는다는 말은 자료가 아니다
한 줄 요약
망 분리 환경에 제품을 넣을 때 "밖으로 나가지 않습니다" 는 문장으로 받아들여지지 않는다. 구성 파일이 무엇을 부르도록 되어 있는지, 기록에 무엇이 실제로 남았는지, 그리고 확인하지 못한 범위가 어디까지인지를 함께 낸 자료만 근거가 된다.
왜 이게 필요했나
인도 막바지에 감리단이 묻는 질문은 늘 비슷하다. "이 제품이 바깥으로 무엇을 보냅니까." 만든 사람은 "아무것도 안 보냅니다" 라고 답하고 싶다. 그런데 요즘 제품에는 업데이트 확인, 라이선스 온라인 검사, 원격 진단 업로드, 충돌 보고, 사용 통계, 시각 동기가 거의 다 들어 있다. 대부분 기본값이 켜짐이고, 대부분 설치 문서의 표에는 없다. 개발 환경에서는 인터넷이 있으니 아무도 눈치채지 못한 채 몇 년이 지난다.
문제는 이 질문이 신뢰의 문제가 아니라 절차의 문제라는 점이다. 감리단은 만든 사람을 의심해서 묻는 것이 아니다. 말로 한 약속은 다음 담당자에게 전달되지 않고, 다음 판올림에서 조용히 뒤집힌다. 그래서 자료로 남길 수 있는 형태를 요구한다. 미국 표준기술연구소의 [SP 800-53 Rev.5](https://csrc.nist.gov/pubs/sp/800/53/r5/upd1/final)는 시스템·통신 보호(SC) 계열에 경계 보호를 두고 "허용한 것만 통과시키고 나머지는 막는다" 를 기본 자세로 적는다. 방산 공급망을 다루는 [SP 800-171 Rev.3](https://csrc.nist.gov/pubs/sp/800/171/r3/final)과 컨테이너 환경을 다루는 [SP 800-190](https://csrc.nist.gov/pubs/sp/800/190/final)도 같은 자리에서 "통신은 기본이 거부" 를 전제한다. 이 전제 아래에서 우리가 낼 수 있는 자료는 "우리 제품이 무엇을 부르는가" 의 전체 목록이다.
어떻게 동작하나
세 층을 각각 읽고 시각으로 잇는다.
첫째, 구성. 제품이 무엇을 부르도록 되어 있는가. 여기서 두 가지가 매번 사람을 속인다. 하나는 주석으로 막아 둔 항목이다. 지운 것이 아니라 가려 둔 것이라 다음 판올림에서 되살아난다. 그래서 꺼짐으로 표에 올리되 목록에서 빼지는 않는다. 다른 하나는 기본값이 켜짐인 항목이다. 설정 파일에 enabled 가 아예 없으면 제품이 켜진 것으로 본다는 규칙이 흔한데, 이것을 모르면 목록에서 통째로 빠진다.
이 두 번째 함정은 도구 쪽에서도 똑같이 난다. jq 의 대체 연산자 // 는 왼쪽이 null 일 때뿐 아니라 false 일 때도 오른쪽을 쓴다. 그래서 .enabled // true 는 "키가 없으면 켜짐" 을 뜻하는 것 같지만, 실제로는 "enabled": false 라고 또박또박 꺼 둔 항목까지 켜진 것으로 만든다. 키가 없는 것과 값이 false 인 것은 다른 사실이고, 그 둘을 구분하는 것이 이 일의 절반이다.
둘째, 기록. 프록시 접속 기록은 실제 시도를 센다. 여기서 응답 코드를 뭉뚱그리면 안 된다. 200 은 요청이 실제로 나갔다는 뜻이고, 407 은 프록시가 인증을 요구해 멈춘 것이며, 403 은 프록시가 막은 것이다. 403 이 열 줄 있다는 것은 "송신이 없었다" 가 아니라 "열 번 나가려 했고 열 번 막혔다" 다. 감리에 내야 하는 사실은 뒤쪽이다.
셋째, 이름 해석. 프록시를 거치지 않는 통신은 프록시 기록에 아무것도 남기지 않는다. UDP 로 도는 시각 동기, 프록시 설정을 읽지 않는 라이브러리, 방화벽에서 곧바로 떨어진 직접 연결이 전부 그렇다. 그런 기능도 이름은 물어본다. 그래서 질의 기록에만 있고 접속 기록에는 없는 목적지가 따로 나오고, 그것이 그 코드 경로가 실제로 돌았다는 유일한 흔적이다. 반대로 구성에 주소가 곧바로 적혀 있으면 질의 기록에도 남지 않는다. [RFC 1918](https://datatracker.ietf.org/doc/html/rfc1918)의 사설 대역 주소라면 밖으로 나갔을 가능성은 낮지만, 허용 목록이 이름으로 적혀 있는 한 그 주소가 어느 서비스인지는 대조할 수 없다. 이런 항목은 허용도 미허용도 아닌 판단 불가로 적고, 확인하지 못한 것에 남긴다.
그리고 시각. 세 기록을 한 표로 이으면 "언제부터 이 시도가 생겼는가" 가 나온다. 형상 변경 기록의 시각과 첫 질의 시각이 붙어 있으면 그 변경이 원인이라고 말할 근거가 된다. 이 표는 반대로도 쓸모가 있다. 변경 기록에 없는데 기록에만 나타나는 목적지는 설치 스크립트나 사람 손이 남긴 것이라, 형상 관리 밖에서 벌어진 일을 드러낸다.
이 실습의 가정
실습 파드에는 커널 권한이 하나도 없다. tcpdump 도 iptables 도 동작하지 않으므로 패킷을 잡아 보이는 길은 애초에 없다. 실제 인도 현장에서도 개발자가 고객 방화벽에 접근하는 일은 거의 없고, 그 자리는 네트워크 팀이 내주는 방화벽 정책 사본과 장비 로그가 채운다. 이 실습은 그 자리를 프록시 접속 기록과 이름 해석 기록으로 대신한다. 설정 파일에서 "절에 enabled 가 없으면 켜진 것으로 본다" 는 제품 규칙도 이 실습의 가정이며, 실제 제품마다 다르므로 현장에서는 반드시 설명서에서 확인한다.
현장에서 만나는 모습
한 인도 건에서 "텔레메트리는 끕니다" 라고 회의록에 적고 실제로도 껐는데, 반년 뒤 판올림에서 기본 설정 파일이 통째로 교체되며 되살아났다. 아무도 몰랐던 이유는 간단하다. 껐다는 사실이 사람의 기억과 회의록에만 있었고, 기계가 확인하는 자리에는 없었기 때문이다. 목록과 다시 수집한 기록을 자료로 남기면 다음 판올림에서 같은 절차를 한 번 더 돌릴 수 있다.
다른 건에서는 "미허용 0건" 을 내려고 기록에서 해당 줄을 지운 경우를 봤다. 이것은 자료가 아니라 자료의 반대다. 없음을 보이는 방법은 지우는 것이 아니라 끄기 전과 끈 뒤 두 벌의 기록을 나란히 두는 것이다. 끈 뒤의 기록에 승인된 목적지가 그대로 남아 있어야 그 수집이 진짜였다는 것도 함께 보인다.
실무에서 진짜 중요한 것
- 확인한 범위와 확인하지 못한 것을 같은 문서에 적는다. 범위를 밝히지 않은 "이상 없음" 은 다음 사람을 속인다.
- 판정만 적지 말고 센 값과 어느 파일에서 왔는지를 함께 남긴다. 감리 담당자가 스스로 다시 셀 수 있어야 한다.
- 꼭 필요한 기능은 끄는 대신 승인된 내부 목적지로 돌린다. 시각 동기를 꺼 버리면 다른 자리에서 사고가 난다.
- 자료는 낡는다. 판올림마다 같은 절차를 다시 돌릴 수 있게 스크립트로 만들어 인계한다.
다음 실습에서 할 것
구성 파일 세 개에서 바깥을 가리키는 주소를 전부 뽑아 켜짐·꺼짐을 가르고, 허용 목록과 대조해 허용·미허용·판단 불가 세 갈래로 나눈다. 프록시 기록에서 시도를 응답별로 세고, 질의 기록에만 있는 목적지를 따로 찾는다. 세 기록을 시각으로 이어 어느 형상 변경이 그 시도를 만들었는지 짚은 뒤, 구성을 실제로 끄고 기록을 다시 수집해 사라졌음을 보인다. 마지막에 감리 제출용 요약을 기계가 읽는 JSON 과 사람이 읽는 표로 함께 낸다.