"지금 어떤 도메인이 서비스되고 있는가" 를 사람이 파일을 열어 세는 방식은 오래가지 않는다. 출처는 대개 두 곳이다. 하나는 DNS 서버의 zone 정의이고, 다른 하나는 Kubernetes 의 Ingress 리소스다. 두 쪽 모두 명령으로 뽑을 수 있으므로 그 출력을 화면에 얹으면 목록 페이지가 된다.
named.conf 를 정규식으로 긁어 zone 을 찾는 코드를 흔히 만든다. 동작은 하지만 include 중첩, 주석, 뷰(view) 안의 zone, 상대 경로 해석에서 어긋난다. BIND 에는 설정을 완전히 전개해 출력하는 도구가 들어 있으므로 그것을 쓴다.
named-checkconf -p /etc/named.conf
include 가 모두 펼쳐지고 주석이 제거된 정규화된 형태가 나온다. 여기서 zone 이름과 파일 경로를 뽑는다.
named-checkconf -p /etc/named.conf \
| awk '/^zone/ {name=$2} /file/ {print name, $2}' \
| tr -d '";'
zone 파일 안의 레코드도 마찬가지로 전용 도구를 쓴다. named-compilezone 이 $ORIGIN · $TTL · 상대 이름 · @ 를 모두 풀어 정규화된 형태로 내보낸다.
named-compilezone -f text -F text -o - example.com /var/named/example.com.zone
출력은 모든 이름이 FQDN 으로 절대화된 한 줄짜리 레코드 목록이라 그대로 파싱하면 된다.
named-compilezone -f text -F text -o - example.com /var/named/example.com.zone \
| awk '$4 ~ /^(A|AAAA|CNAME)$/ {print $1, $4, $5}'
동적 갱신(DDNS)을 쓰는 zone 은 파일에 최신 내용이 없을 수 있다. 저널이 반영되도록 먼저 동기화한다.
rndc sync -clean example.com
Ingress 는 spec.rules[].host 와 spec.tls[].hosts[] 두 군데에 도메인을 담는다. 둘을 합쳐야 전체가 된다.
kubectl get ingress -A -o json | jq -r '
.items[] |
. as $i |
( [ .spec.rules[]?.host ] + [ .spec.tls[]?.hosts[]? ] | unique )[] |
"\($i.metadata.namespace)\t\($i.metadata.name)\t\(.)"
'
백엔드 서비스까지 함께 보려면 경로별 backend 를 펼친다.
kubectl get ingress -A -o json | jq -r '
.items[] |
.metadata.namespace as $ns |
.spec.rules[]? |
.host as $h |
.http.paths[]? |
"\($ns)\t\($h)\t\(.path)\t\(.backend.service.name):\(.backend.service.port.number // .backend.service.port.name)"
'
게이트웨이 API 를 쓰는 클러스터라면 Ingress 만 봐서는 누락된다. HTTPRoute 의 spec.hostnames 도 함께 모은다.
목록 페이지를 클러스터 밖 서버에서 돌린다면 읽기 전용 서비스 계정을 만들어 그 토큰으로 kubeconfig 를 구성한다. 사람 계정의 인증서를 복사해 쓰지 않는다.
apiVersion: v1
kind: ServiceAccount
metadata:
name: ingress-reader
namespace: automation
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: ingress-reader
rules:
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: ingress-reader
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: ingress-reader
subjects:
- kind: ServiceAccount
name: ingress-reader
namespace: automation
내장 view 클러스터롤을 그대로 붙이는 예제가 많지만, view 는 ConfigMap 을 비롯해 훨씬 많은 것을 읽을 수 있다. 도메인 목록만 필요하면 위처럼 좁게 준다.
작은 Flask 앱으로 위 출력을 JSON 으로 내보내고 화면에서 주기적으로 새로 고치는 구성이 흔하다. 만들기는 쉽지만 다음을 빼먹으면 곤란해진다.
인증을 붙인다. 내부 도메인 목록은 그 자체로 내부 구조를 드러낸다. 인증 없이 열어 두지 않는다.
호출 주기를 생각한다. 30초마다 전체 Ingress 를 조회하면 API 서버에 꾸준한 부하가 간다. 결과를 캐시하고 캐시 만료 때만 실제 조회한다. DNS 쪽도 zone 이 많으면 매 요청마다 named-compilezone 을 돌리지 않는다.
최소 권한으로 돌린다. zone 파일을 읽으려면 BIND 파일에 대한 읽기 권한이 필요하다. 앱을 root 로 돌리지 말고 전용 계정에 읽기 권한만 준다. 경로를 요청 파라미터로 받아 그대로 여는 코드는 경로 탐색 취약점이 되므로, 설정에서 뽑은 목록 안의 경로만 허용한다.
읽기 전용으로 만든다. 조회 페이지에 수정 기능을 붙이는 순간 권한과 검증 요구가 완전히 달라진다.