Haru Utils

네트워크 · 구성 레시피

systemd-resolved·BIND 로컬 DNS 구성

systemd-resolved의 로컬 stub 역할과 BIND의 캐시·전달 역할을 분리하고, 포트 충돌·재귀 공개·zone transfer를 차단한 루프백 DNS 체인을 검증합니다.
systemd-resolved 설정BIND9 설치DNS 캐시 서버DNS 포트 충돌allow-recursion
지원 환경Ubuntu Server 24.04 LTS와 systemd-resolved
예상 시간50
난이도고급
검토일2026-08-28

BEFORE YOU START

시작 전에 준비하세요

01

콘솔 또는 IP 기반 SSH 세션과 기존 DNS 설정의 백업

02

승인된 upstream DNS 두 개와 해당 경로의 UDP·TCP 53 허용

03

현재 /etc/resolv.conf 관리 주체와 per-link DNS·검색 도메인 기록

04

권위 DNS가 필요하다면 이 캐시 서버와 분리한다는 설계 결정

권장 대상 애플리케이션의 로컬 DNS 안정성을 높이되 공개 재귀 DNS나 포트 충돌을 만들지 않으려는 운영자

FOLLOW THE RECIPE

8단계 구성·점검 레시피

1
확인시스템을 변경하지 않는 점검 단계

DNS 구성 주체와 역할 확인

systemd-resolved는 애플리케이션이 질의하는 로컬 stub, BIND는 루프백에서만 받는 전달 캐시로 역할을 분리합니다. 외부 권위 DNS를 이 레시피에 섞지 않습니다.

운영체제
cat /etc/os-release
resolv.conf 연결
ls -l /etc/resolv.conf
resolved 상태
resolvectl status
DNS 소켓
sudo ss -lntup | grep -E ':(53|1053) '
  • /etc/resolv.conf가 systemd-resolved stub 또는 uplink 파일로 관리되는지 확인했다.
  • 현재 per-link DNS와 search domain을 기록했다.
  • BIND는 권위 서버가 아닌 로컬 전달 캐시로 사용한다.
결과 읽기

NetworkManager·컨테이너 런타임·수동 resolv.conf가 관리 주체라면 symlink를 임의 변경하지 말고 해당 도구의 공식 통합을 사용합니다.

다음 판단

53번과 로컬 BIND용 1053번 포트, upstream 경로를 확인합니다.

2
확인시스템을 변경하지 않는 점검 단계

포트·upstream·현재 조회 사전 점검

resolved의 53번 stub은 유지하고 BIND는 1053번 루프백을 사용합니다. mDNS 5353과도 겹치지 않습니다.

53·1053 포트 소유자
sudo ss -lntup | grep -E ':(53|1053) '
현재 시스템 조회
resolvectl query example.org
upstream 1 직접 조회
dig @<UPSTREAM_DNS1> example.org A +time=2 +tries=1
upstream 2 TCP 조회
dig @<UPSTREAM_DNS2> example.org A +tcp +time=2 +tries=1
  • 127.0.0.53:53은 resolved가 사용하고 127.0.0.1:1053은 비어 있다.
  • 두 upstream DNS가 UDP와 필요한 TCP 조회에 응답한다.
  • 현재 정상 조회 결과와 TTL을 기준값으로 기록했다.
결과 읽기

upstream 직접 조회가 실패하면 로컬 DNS를 바꾸지 말고 outbound 방화벽·라우팅을 먼저 해결합니다.

다음 판단

BIND 도구를 배포판 패키지로 설치합니다.

3
변경패키지·설정·서비스 상태가 달라지는 단계

BIND와 진단 도구 설치

Ubuntu 저장소의 bind9·bind9-utils·dnsutils를 설치합니다. 아직 resolved의 upstream은 변경하지 않습니다.

변경 단계입니다. 대상 서버, 백업 파일, 서비스 중단 영향과 바로 이전 상태로 돌아가는 방법을 다시 확인하세요.
APT 인덱스
sudo apt update
변경 미리 보기
apt-get --simulate install bind9 bind9-utils dnsutils
신규 BIND 자동 시작 차단
sudo systemctl mask named.service bind9.service
precheck에서 기존 BIND가 없음을 확인한 신규 설치 분기에서만 실행합니다. 기존 DNS 서비스에서는 설치 절차를 중단합니다.
BIND 설치
sudo apt-get install bind9 bind9-utils dnsutils
설치 직후 비노출 확인
systemctl is-enabled named.service bind9.service
sudo ss -lntup | grep -E ':(53|1053)[[:space:]]'
named·bind9가 masked이고 신규 BIND 소켓 출력이 없어야 합니다. resolved의 127.0.0.53:53은 유지됩니다.
버전 확인
named -V
dig -v
  • 의도하지 않은 DNS·네트워크 패키지 제거가 없다.
  • BIND와 dig 버전을 기록했다.
  • 설치만으로 외부 인터페이스에 53번이 열리지 않았는지 확인했다.
결과 읽기

패키지 설치 직후 BIND가 넓은 주소에 수신 중이면 다음 구성 전 임시 접근통제를 확인하고 외부 공개 상태로 두지 않습니다.

다음 판단

BIND를 루프백 1053에 제한하고 resolved drop-in을 준비합니다.

4
주의권한·연결·중단 영향을 확인할 단계

루프백 재귀 제한과 resolved 연결 구성

BIND의 질의·캐시·재귀를 localhost로 제한하고 zone transfer를 명시적으로 거부합니다. upstream은 IP로 지정해 resolver loop를 방지합니다.

백업 접미사
date -u +%Y%m%dT%H%M%SZ
BIND 옵션 백업
sudo cp --preserve=all --no-clobber /etc/bind/named.conf.options /etc/bind/named.conf.options.<BACKUP_SUFFIX>.bak
resolved drop-in 디렉터리
sudo install -d -m 0755 -o root -g root /etc/systemd/resolved.conf.d
기존 resolved drop-in 백업
sudo cp --preserve=all --no-clobber /etc/systemd/resolved.conf.d/60-local-bind.conf /etc/systemd/resolved.conf.d/60-local-bind.conf.<BACKUP_SUFFIX>.bak
기존 파일이 있을 때만 실행합니다.
BIND 옵션 편집
sudoedit /etc/bind/named.conf.options
resolved drop-in 편집
sudoedit /etc/systemd/resolved.conf.d/60-local-bind.conf
설정 예시 · /etc/bind/named.conf.options
루프백 전용 전달 캐시
options {
  directory "/var/cache/bind";
  listen-on port 1053 { 127.0.0.1; };
  listen-on-v6 port 1053 { ::1; };

  recursion yes;
  allow-query { localhost; };
  allow-query-cache { localhost; };
  allow-recursion { localhost; };
  allow-transfer { none; };

  forward only;
  forwarders {
    <UPSTREAM_DNS1>;
    <UPSTREAM_DNS2>;
  };

  dnssec-validation auto;
  minimal-responses yes;
};
upstream은 IP 주소로 넣고 서로 다른 장애 도메인의 승인된 DNS를 사용합니다. 외부 클라이언트를 추가하지 않습니다.
설정 예시 · /etc/systemd/resolved.conf.d/60-local-bind.conf
resolved가 로컬 BIND를 사용하도록 연결
[Resolve]
DNS=127.0.0.1:1053
FallbackDNS=
Domains=~.
DNSStubListener=yes
127.0.0.53의 stub을 유지합니다. 이 drop-in은 BIND 직접 질의가 성공한 뒤에만 반영합니다.
  • BIND는 127.0.0.1·::1의 1053만 수신한다.
  • allow-query·allow-query-cache·allow-recursion은 localhost뿐이다.
  • allow-transfer none과 IP 기반 forwarders가 있다.
  • resolved stub 53을 끄지 않았다.
결과 읽기

공개 재귀나 권위 zone이 필요하다면 이 구성에 주소를 넓혀 추가하지 말고 별도 서버·ACL·TSIG·DNSSEC 설계를 사용합니다.

다음 판단

named 문법과 직접 1053 조회를 먼저 검증한 뒤 resolved를 전환합니다.

5
주의권한·연결·중단 영향을 확인할 단계

BIND 선검증 후 resolved 전환

IP 기반 기존 관리 세션을 유지합니다. BIND 문법과 직접 조회가 성공하지 않으면 resolved를 재시작하지 않습니다.

BIND 전체 문법
sudo named-checkconf
검사 후 BIND 시작
sudo named-checkconf && sudo systemctl unmask named.service bind9.service && sudo systemctl enable --now named.service
BIND 직접 조회
dig @127.0.0.1 -p 1053 example.org A +dnssec +time=2 +tries=1
resolved 유효 구성 보기
systemd-analyze cat-config systemd/resolved.conf
직접 조회 성공 후 resolved 재시작
dig @127.0.0.1 -p 1053 example.org A +time=2 +tries=1 && sudo systemctl restart systemd-resolved.service
  • named-checkconf가 성공한 뒤에만 BIND를 시작했다.
  • 1053 직접 조회가 성공한 뒤에만 resolved를 재시작했다.
  • 기존 IP 기반 세션과 콘솔을 유지한다.
결과 읽기

직접 조회 실패 시 resolved는 기존 상태로 두고 BIND journal·forwarder·outbound 53을 확인합니다.

다음 판단

시스템 stub과 직접 BIND 결과, 외부 노출 여부를 검증합니다.

6
확인시스템을 변경하지 않는 점검 단계

stub·캐시·포트 경계 검증

resolvectl 경로와 BIND 직접 경로를 비교하고, 별도 호스트에서 1053에 접근할 수 없는지 확인합니다.

resolved 전체 상태
resolvectl status
시스템 조회
resolvectl query example.org
BIND 직접 재조회
dig @127.0.0.1 -p 1053 example.org A +stats +time=2 +tries=1
수신 주소
sudo ss -lntup | grep -E ':(53|1053) '
서비스 상태
systemctl is-active bind9.service systemd-resolved.service
  • resolvectl 조회와 직접 BIND 조회가 모두 성공한다.
  • 53은 resolved stub, 1053은 루프백 BIND만 수신한다.
  • 별도 호스트에서 서버의 1053으로 질의할 수 없다.
  • 재부팅 후에도 같은 역할 분리가 유지된다.
결과 읽기

직접 BIND는 되고 resolvectl만 실패하면 resolved drop-in을, 둘 다 실패하면 BIND·upstream을 확인합니다.

다음 판단

재귀·전송·DNSSEC·로그 노출을 최종 점검합니다.

7
주의권한·연결·중단 영향을 확인할 단계

재귀·zone transfer·노출 최종 점검

공개 재귀 DNS는 증폭 공격에 악용될 수 있습니다. localhost ACL과 loopback bind를 둘 다 유지하며 방화벽은 추가 방어선으로 봅니다.

BIND 문법 재확인
sudo named-checkconf
유효 제한 항목
sudo grep -E 'listen-on|allow-query|allow-query-cache|allow-recursion|allow-transfer|forwarders' /etc/bind/named.conf.options
공개 소켓 재확인
sudo ss -lntup
DNS 오류 로그
sudo journalctl -u bind9.service -u systemd-resolved.service --since '-30 minutes' --no-pager
  • 재귀와 캐시는 localhost만 허용한다.
  • zone transfer는 none으로 거부한다.
  • upstream DNS는 승인된 IP이고 resolver loop가 없다.
  • BIND 제어 채널이나 통계 채널을 외부에 열지 않았다.
  • 권위 DNS·동적 업데이트는 이 서버에 추가하지 않았다.
결과 읽기

외부 주소에서 1053이 보인다면 설정의 listen-on과 컨테이너·NAT 포트 게시를 즉시 확인합니다.

다음 판단

문제 시 resolved를 이전 DNS로 먼저 되돌린 뒤 BIND를 복원합니다.

8
주의권한·연결·중단 영향을 확인할 단계

resolved 선복원 후 BIND 원복

클라이언트 DNS 경로인 resolved를 먼저 이전 상태로 복원합니다. 조회가 회복된 뒤에만 BIND를 중지하거나 설정을 복원합니다.

현재 DNS 상태 보존
resolvectl status
sudo journalctl -u bind9.service -u systemd-resolved.service -n 120 --no-pager
신규 resolved drop-in 비활성화
sudo mv /etc/systemd/resolved.conf.d/60-local-bind.conf /etc/systemd/resolved.conf.d/60-local-bind.conf.disabled.<ROLLBACK_SUFFIX>
이전 resolved drop-in 복원
sudo cp --preserve=all /etc/systemd/resolved.conf.d/60-local-bind.conf.<BACKUP_SUFFIX>.bak /etc/systemd/resolved.conf.d/60-local-bind.conf
작업 전 실제 백업이 있었던 경우에만 실행합니다.
resolved 재시작과 조회
sudo systemctl restart systemd-resolved.service && resolvectl query example.org
현재 BIND 옵션 보존
sudo mv /etc/bind/named.conf.options /etc/bind/named.conf.options.failed.<ROLLBACK_SUFFIX>
이전 BIND 옵션 복원·검증
sudo cp --preserve=all /etc/bind/named.conf.options.<BACKUP_SUFFIX>.bak /etc/bind/named.conf.options && sudo named-checkconf && sudo systemctl reload bind9.service
신규 BIND 중지 분기
sudo systemctl stop named.service
작업 전 BIND가 없었고 resolved가 이전 DNS로 정상 복원된 경우에만 실행합니다.
  • IP 기반 세션 또는 콘솔에서 작업했다.
  • resolved 이전 경로와 실제 조회를 먼저 복원했다.
  • BIND 백업이 있던 경우에만 복원하고 문법 검사 후 reload했다.
  • 53·1053 소켓과 resolvectl 상태를 다시 확인했다.
결과 읽기

resolved 복원 후에도 조회가 안 되면 /etc/resolv.conf 링크와 per-link DNS 관리자를 확인하고 BIND 변경과 분리합니다.

다음 판단

원인을 포트 충돌·resolved·BIND·upstream으로 나눠 수정합니다.

SECURITY CHECK

운영 전 마지막 보안 점검

  • systemd-resolved의 stub 53과 BIND 로컬 1053 역할을 분리한다.
  • BIND listen·query·cache·recursion을 모두 localhost로 제한한다.
  • zone transfer를 명시적으로 거부하고 권위 zone을 섞지 않는다.
  • IP 기반 upstream을 사용해 resolver loop를 막는다.
  • BIND 직접 조회 성공 뒤에만 resolved를 전환한다.

COMMON ERRORS

자주 막히는 지점

BIND가 포트 사용 중 오류로 시작하지 않음

증상
address already in use와 함께 bind9가 failed 상태입니다.
가능한 원인
resolved의 53, mDNS의 5353 또는 다른 resolver와 같은 포트를 선택했을 수 있습니다.
확인 순서
ss로 소유자를 확인하고 BIND를 루프백 1053으로 유지합니다. 기존 resolver를 비활성화하지 않습니다.
트러블슈팅으로 이어보기

resolvectl만 SERVFAIL

증상
dig @127.0.0.1 -p 1053은 되지만 시스템 조회는 실패합니다.
가능한 원인
resolved drop-in 문법·적용·/etc/resolv.conf 모드가 일치하지 않을 수 있습니다.
확인 순서
cat-config와 resolvectl status, resolv.conf 링크를 확인해 이전 DNS로 먼저 복원합니다.
트러블슈팅으로 이어보기

BIND 직접 조회도 timeout 또는 SERVFAIL

증상
1053 직접 질의가 응답하지 않거나 DNSSEC 검증 오류가 납니다.
가능한 원인
upstream UDP·TCP 53 차단, 잘못된 forwarder, 시간 동기화 또는 DNSSEC 문제일 수 있습니다.
확인 순서
upstream 직접 dig, 라우팅, 시스템 시간, BIND journal 순서로 확인합니다.
트러블슈팅으로 이어보기

PRIMARY REFERENCES

공식 문서

설치 저장소와 지원 버전은 바뀔 수 있습니다. 검토일 이후에는 링크된 공식 문서와 현재 서버의 패키지 후보 버전을 함께 확인하세요.

도구 빠른 검색

최근 사용한 도구를 다시 열거나, 이름과 기능으로 검색하세요.

검색어와 도구의 입력·결과는 저장하지 않습니다.

↑↓ 이동 · Enter 열기 · Esc 닫기