--- kind: QUESTION slug: guest-input-hole-under-the-iptables-backend title: libvirt 의 firewall_backend 가 iptables 일 때도 guest_input 에 구멍이 필요한가 topic: lab-environment-build topicName: 실험대 환경 구성 project: virtualization status: 게시 전 questionStatus: OPEN sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6 source: - final/document.md#183-이-부에서-파생될-open-question-oq-1 - final/document.md#180-nftables-는-앞-체인의-accept-로-뒤-체인의-reject-를-막지-못한다 --- # libvirt 의 firewall_backend 가 iptables 일 때도 guest_input 에 구멍이 필요한가 libvirt 의 firewall_backend 가 iptables 일 때도 guest_input 에 구멍이 필요한지는 재지 않았다. nftables 백엔드에서는 그 체인의 reject 가 밖에서 온 요청을 connection refused 로 끊었고, forward 체인의 accept 도 막지 못했다. 백엔드만 바꿔 재면 닫힌다. ## 관계 - **호스트 안에서는 404, 밖에서는 connection refused — 앞 체인의 accept 가 libvirt 의 reject 를 막지 못했다** 그 기록이 nftables 백엔드에서 증상과 원인과 해결을 닫았고, 이 물음은 거기서 미확인으로 표시된 한 줄을 받는다. - **엣지 nginx 를 호스트에서 게스트 VM 으로 옮긴다 — 성능이 아니라 더러워지는 층의 격리** 그 결정이 감수한 일곱 가지 중 3번이 libvirt 방화벽에 구멍을 뚫는 일이라, 이 물음의 답이 그 비용의 적용 범위를 정한다. - **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge** 밖에서 들어온 패킷이 게스트까지 가는 경로를 그 기록이 설명한다. - **이 호스트의 가상 머신 네트워크는 Bridge 인가 NAT 인가 Routing 인가** 밖에서 게스트로 들어오는 경로가 FORWARD 를 타는지가 그 모드에서 갈린다. ## 사실 - 제3부가 그린 게스트 패킷 경로 위에서 이 구축이 가장 오래 막힌 지점이 여기라고 §180 이 적었다. - 호스트에서 curl http://192.168.122.10 을 치면 엣지 nginx 가 404 로 응답했다. - 밖에서 curl http://100.83.212.4 을 치면 connection refused 가 왔다. 드롭이면 기다리다 죽으니, 타임아웃이 아니라 즉시 거절이라는 점이 단서였다. - libvirt 는 자기 테이블 ip libvirt_network 의 guest_input 체인을 reject 로 끝낸다. 그 앞에는 established,related 를 accept 하는 규칙이 있다. - 그 reject 규칙의 카운터가 4 패킷 240 바이트였고 밖에서 친 curl 횟수와 정확히 일치했다. 범인은 그 숫자로 확정했다. - DNAT 파일에는 priority filter - 10 으로 먼저 도는 forward 체인이 있고 거기에 ct state new accept 를 넣어 두었다. 그런데도 패킷은 뒤 체인에서 거절됐다. - nftables 는 같은 훅에 붙은 base 체인을 우선순위 순으로 전부 평가한다. 앞 체인의 accept 는 이 체인은 통과라는 뜻이고, 평가를 즉시 끝내는 것은 drop 이다. iptables 감각으로 쓰면 정확히 여기서 틀린다. - 구멍은 libvirt 체인 맨 앞에 뚫었다. insert 가 맨 앞이고 add 가 맨 뒤다. - 그 규칙은 휘발성이다. libvirt 가 네트워크를 다시 세우면 guest_input 을 새로 쓰면서 날아가므로 DNAT 유닛의 ExecStartPost 에 넣었다. - 이 호스트는 nftables 백엔드다. firewall_backend 가 iptables 일 때도 같은지는 재지 않았다고 §180 이 미확인으로 표시했다. ## 가정 - 백엔드를 iptables 로 바꿔도 libvirt 가 밖에서 게스트로 들어오는 경로에 자기 규칙을 만든다고 본다. 확인한 것은 nftables 백엔드 하나뿐이라, 규칙을 쓰는 도구만 달라지고 libvirt 가 그 경로를 거른다는 것 자체는 같다고 전제한다. - 엣지 게스트와 DNAT 구성은 그대로 두고 백엔드만 바꾼다고 본다. 둘을 같이 바꾸면 결과가 어느 쪽 때문인지 가려지지 않는다. - §178 이 적은 test-server 한 대에서 잰다고 전제한다. 다른 배포판이나 다른 libvirt 버전에서 같은 결과가 나오는지는 이 물음이 묻지 않는다. - 밖에서 치는 경로는 전과 같다고 본다. §180 의 측정이 밖에서 100.83.212.4 로 친 요청이었으므로 같은 주소를 같은 방법으로 친다. ## 미지수 - firewall_backend 를 iptables 로 둔 호스트에서 밖에서 게스트로 가는 FORWARD 경로를 무엇이 끝내는지. - 그때 forward 체인의 ct state new accept 가 실제로 먹는지, 아니면 거기서도 libvirt 쪽 규칙에 구멍을 따로 뚫어야 하는지. - 구멍이 필요하다면 그 방법이 nftables 에서 쓴 insert 맨 앞 규칙과 어떻게 다른지, 그리고 그 규칙도 네트워크를 다시 세우면 날아가는지. ## 제약 - 잴 수 있는 호스트가 한 대다. §178 이 적은 test-server 는 Arch Linux, i5-1135G7 논리 코어 8, RAM 11,648MiB, QEMU 11.1.1 · libvirt 12.7.0 이다. - 이더넷 없이 WiFi 만 있어 브리지를 못 쓰고 libvirt NAT(virbr0) + 호스트 진입 구조를 택했다. 밖에서 들어온 패킷이 FORWARD 를 타는 것이 그 구성 때문이라, 네트워크 모드가 달라지면 이 물음이 묻는 상황도 달라진다. - 백엔드를 바꾸려면 libvirt 네트워크를 다시 세워야 하고, 그때 guest_input 에 뚫어 둔 구멍이 날아간다. 엣지 게스트가 밖에서 들어오는 요청을 받는 진입점이므로 되돌릴 절차를 먼저 준비한다. - 이 물음에 쓸 측정 원문이 아직 없다. §180 의 curl 출력도 규칙 덤프도 final/evidence/ 에 없어서, 카운터 4 패킷 240 바이트의 근거는 SSOT 본문에 옮겨 적힌 덤프뿐이다. ## 선택지 ### 1. 구멍을 뺀 채 백엔드만 iptables 로 바꾸고 밖에서 한 번 친다 §183 이 적은 그대로다. 백엔드를 바꾸면서 libvirt 네트워크를 다시 세우면 guest_input 의 구멍은 어차피 날아가기 때문에, 구멍 없는 상태가 저절로 만들어진다. 그 상태에서 밖에서 엣지로 curl 을 치고 응답인지 connection refused 인지, 그리고 거기까지 걸린 시간을 적는다. 같은 시각에 libvirt 가 만든 규칙을 그대로 덤프해 어느 규칙이 패킷을 끝내는지 본다. 규칙마다 카운터가 붙어 있으면 그 값이 친 횟수와 맞는지도 함께 읽는다. nftables 에서 범인을 지목한 것이 그 카운터였다. §180 이 적은 그 forward 체인은 지금 DNAT 파일에 없다. §189 가 싣는 파일에 남은 체인은 prerouting 하나이고, accept 는 유닛의 ExecStartPost 가 libvirt 체인 안에 넣는다. 그래서 아래 셋 가운데 마지막을 재려면 그 체인을 먼저 되돌려 놓아야 한다. 읽어 낼 것은 셋이다. 밖에서 친 요청의 결과 : 응답인가 connection refused 인가 패킷을 끝낸 규칙 : 어느 테이블의 어느 체인인가 forward 체인의 accept : 먹었는가 먹지 않았는가 ### 2. 구멍을 남겨 둔 채 백엔드만 바꾼다 — 제외 구멍이 이미 열려 있으면 요청은 통과하고, 그 통과가 구멍 덕분인지 백엔드가 원래 막지 않아서인지 가려지지 않는다. ### 3. libvirt 가 iptables 백엔드에서 만드는 규칙을 문서로 읽어 견준다 — 제외 호스트를 건드리지 않아도 되지만 답이 나오지 않는다. §180 에서도 규칙 목록이 아니라 카운터가 범인을 지목했으니, 우선순위가 다른 base 체인 둘이 한 훅에 붙었을 때 어느 쪽이 패킷을 끝내는지는 이 구성에서 돌려 봐야 나온다. ## 다음 검증 1. libvirt 의 firewall_backend 를 iptables 로 두고 네트워크를 다시 세운다. 이때 guest_input 에 뚫어 둔 구멍이 날아가므로 구멍 없는 상태에서 시작한다. 2. 밖에서 엣지로 curl 을 치고 결과가 응답인지 connection refused 인지, 그리고 거기까지 걸린 시간을 적는다. 3. 같은 시각에 libvirt 가 만든 규칙을 그대로 덤프해 어느 규칙이 패킷을 끝내는지와 그 카운터가 친 횟수와 맞는지를 본다. 4. 출력 원문은 final/evidence/raw/ 에 남기고, 그 실행의 명령과 cwd 와 실행 시각과 종료 코드는 meta/ 에 적는다. 5. 백엔드를 nftables 로 되돌리고 DNAT 유닛의 ExecStartPost 가 구멍을 다시 뚫는지 확인한다. 닫는 조건 : 백엔드를 iptables 로 둔 상태에서 밖에서 친 요청이 응답을 받으면 그 백엔드에서는 구멍이 필요 없다고 적고 닫는다. 여전히 거절되면 어느 규칙이 끝냈는지와 그때의 구멍 방법을 「호스트 안에서는 404, 밖에서는 connection refused」 기록의 해결 절에 행으로 더한 뒤 닫는다. 어느 쪽이든 그 결과가 엣지를 게스트로 옮기며 감수한 비용 3번의 적용 범위를 정한다. 지금 그 비용은 nftables 백엔드에서만 확인했다.