Linux iptables를 통한 DNS 서버 고정

(Last Updated On: June 2, 2020)

리눅스에서 NetworkManager 등에 의해 IP가 관리되는 경우 DNS (/etc/resolv.conf) 를 수정하는 일이 굉장히 번거롭다. 뭔가 이상하지만 iptables를 통해 DNS 서버를 필터링 해서 IP를 고정시켜보겠다.

 

iptables

  • -t nat : 주소변환을 하는 테이블 리스트에 추가
  • -I (맨위) OUTPUT : 인터페이스에서 나가는 트래픽에 대한 체인
  • -p udp : 프로토콜이 UDP 인 경우
  • ! –destination 8.8.0.0/16 : 대상 아이피가 8.8.0.0~8.8.255.255 가 아닌 경우
  • –dport 53 : 대상이 53포트 (DNS)인 경우
  • -j DNAT : 목적지 주소를 변경한다.
    –to-destination=8.8.8.8 : 목적지 주소는 8.8.8.8로 변경한다.

Verify

K 모사의 DNS 서버로 질의를 보내면 아웃풋 인터페이스에서 8.8.8.8 로 찍히는 것을 볼 수 있다.

문제점 및 해결방안

53 PORT

DNS IP를 제외한 모든 트래픽을 8.8.8.8:53으로 보내는 경우 53port의 VPN서버등에 연결이 문제가 될 수 있다.  따라서 사용될 가능성이 있는 DNS IP만을 셋팅하는 것이 좋은 방법이다.

Secondary DNS

위 룰의 경우 문제가 단일DNS 서버를 사용해서 8.8.8.8 에 문제가 생기면 결론적으로 네트워크가 죽게 될 것이다. 따라서 Secondary DNS서버를 설정해야한다. 어차피 DNS 요청은 클라이언트(운영체제 시스템)가 설정한 DNS서버갯수만큼 여러 개를 날리니까 리눅스 쪽에서는 라운드로빈 방식으로 DNS서버를 교체하면 충분하다.

round-robin으로 처리하는 방법

인터넷을 찾아보았다. -m statistic --mode nth --every 2  을 입력하면 패킷이 2번째마다 ~로 처리되는 기술을 갖는 것 같다. nth patch로 불리는 이 netfilter 확장기능.

이렇게 하면 168.126.63.0/30에 대한 트래픽을 8.8.8.8/1.1.1.1 번갈아 처리될 것이다. 일반 호스트는 설정된 DNS 서버 (보통 2개) 갯수만큼 DNS를 동시에 요청하기 때문에 round-robin은 성공적이라고 생각된다. 주의할 점은 -m statistic --mode nth --every 2  가 없는 룰보다는 우선적으로 있어야한다.  --packet 0  은 0번째 패킷을 사용하는건가본데..? iptables툴이 –packet을 넣으라고 해서 0을 넣는다 iptables는 위에서 아래로 처리 (점프)되기 때문에

TCP 문제

OUTPUT 연결 룰에서 TCP는 현재 연결된 서버의 IP의 Destination IP 까지 변경해 버릴 우려가 있다. 따라서 현재 연결되어있는 TCP 세션에는 목적지 주소를 변경하지 않도록 룰을 점프해야한다.

또는

핵심은 현재 연결되어있는 TCP는 OUTPUT체인에서 점프시켜야한다. 따라서 최상위 룰( -I )로 적용한다.

Verify

성공적으로 목적지 아이피를 변경하였다. 

udp로는 전송되지 못하는 대용량의 데이터도 TCP로 정상적으로 받는게 가능하다.

 

마치며

이번 DNS 수정은 결코 DNS캐시서버의 신뢰성?문제나 특정이유로 인해 DNS서버를 고정해야하는 경우에나 사용하지, DNS 데이터를 안전하게 전송 하려는 목적으로는 쓸 수 없다. DNS 프로토콜은 암호화 되지 않기 때문에 암호화를 사용하려면 DoH나 DoT등을 사용하고 해당 기술을 사용하면 서버에 대한 인증까지 포함되기 때문에 IP를 멋대로 바꿔서는 안 된다.