Skip to content
Published on

폐쇄망 반입 절차와 무결성 — 6개월 뒤에도 같은 설치가 나오는 번들 만들기

공유하기
Authors

들어가며 — 폐쇄망에서 서명 검증이 더 중요한 이유

연결망에서는 무언가 이상하면 다시 받아 보면 됩니다. 해시가 안 맞으면 원본과 대조하고, 서명이 이상하면 배포처에 확인합니다. 비교 대상이 언제나 손에 닿는 곳에 있습니다.

폐쇄망은 그 전제가 깨집니다. 매체가 안으로 들어가는 순간, 원본과 대조할 방법이 사라집니다. 6개월 뒤 "이 rpm 어디서 온 거죠"라는 질문이 나왔을 때 답할 근거가 그 매체 안에 같이 들어 있지 않으면 영원히 답할 수 없습니다.

그래서 폐쇄망에서 무결성 검증은 보안 요구이기 이전에 운영 요구입니다. 이번 편은 그 근거를 번들 안에 함께 넣는 방법입니다.

무엇을 매체에 담는가

반입 단위에 들어가야 하는 것은 rpm 파일만이 아닙니다. 최소한 이 여섯 가지입니다.

  1. rpm 파일 묶음 — 2편에서 만든 것
  2. repodata — 3편에서 생성한 저장소 메타데이터
  3. GPG 공개키 — 안쪽에서 서명을 검증하려면 필요합니다
  4. 매니페스트 — 무엇이 들어 있는지, 어느 시점 것인지
  5. 체크섬 파일 — 매니페스트와 별개로 매체 무결성 확인용
  6. 설치 절차서 — 어떤 순서로 무엇을 실행하는지

3번을 빠뜨리면 안쪽에서 gpgcheck=1이 첫 설치를 막습니다. 4번을 빠뜨리면 재현이 불가능해집니다. 실제로 가장 자주 빠지는 것이 이 둘입니다.

서명을 검증한다

rpm 서명 검사는 패키지의 다이제스트와 서명을 함께 확인합니다. 최신 업스트림 man 페이지는 이 기능을 rpmkeys에 두고, rpm-K, --checksig, --import는 "Obsolete compatibility aliases"로 분류합니다. RHEL 8·9·10에서는 양쪽 표기가 모두 동작하지만, 새 절차서라면 rpmkeys 쪽을 쓰는 편이 낫습니다.

# 키를 먼저 등록해야 서명 검증이 의미를 갖습니다
sudo rpmkeys --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release

# 번들 전체의 서명과 다이제스트를 검사한다
rpmkeys --checksig /var/tmp/airgap-bundle/rpms/*.rpm

# 실패한 것만 추려서 본다
rpmkeys --checksig /var/tmp/airgap-bundle/rpms/*.rpm | grep -v 'digests signatures OK'

rpmkeys man 페이지는 -K, --checksig를 "Verify the digests and signatures contained in PACKAGE_FILE to ensure the integrity and origin of the package"로 정의합니다. 두 번째 절이 핵심입니다. 다이제스트는 무결성을, 서명은 출처를 보증합니다. 둘은 다른 질문에 답하며 둘 다 필요합니다.

키를 등록하지 않은 상태에서 검사하면 다이제스트는 통과하고 서명은 확인 불가로 나옵니다. 이 상태를 "통과"로 읽는 실수가 흔하므로, 검사 전에 rpmkeys --list로 키가 등록되어 있는지 먼저 봐야 합니다.

받는 단계에서 미리 거를 수도 있습니다. 2편에서 본 dnf reposync-g, --gpgcheck는 "Remove packages that fail GPG signature checking after downloading. Exit code is 1 if at least one package was removed"이므로, 이 옵션을 쓰면 서명이 깨진 패키지가 애초에 번들에 들어오지 않고 종료 코드로 알려 줍니다.

재현 가능한 번들 — 매니페스트를 만듭니다

여기가 이번 편의 핵심입니다.

번들이 재현 가능하다는 것은, 같은 매니페스트를 들고 6개월 뒤에 같은 설치 결과를 얻을 수 있다는 뜻입니다. 그러려면 매니페스트에 최소한 다섯 가지가 있어야 합니다.

  • 완전한 NEVRA — 이름, epoch, 버전, 릴리스, 아키텍처
  • 패키지별 체크섬 — SHA-256
  • 저장소 스냅샷 날짜 — 언제 받았는가
  • 사용한 릴리스 버전--releasever에 무엇을 넘겼는가
  • 출처 저장소 ID — 어느 저장소에서 왔는가

epoch를 굳이 넣는 이유가 있습니다. RPM 태그 문서는 Epoch를 "Package epoch (optional). An absent epoch is equal to epoch value 0"으로 정의하는데, 파일 이름에는 epoch가 들어가지 않습니다. 그래서 파일 이름만으로 만든 목록은 epoch가 다른 두 패키지를 구분하지 못합니다. 버전 비교에서 epoch가 가장 우선하므로, 이 차이는 조용하고 치명적입니다.

매니페스트는 rpm 질의로 한 번에 뽑습니다.

#!/usr/bin/env bash
# make-manifest.sh — 번들 디렉터리에서 재현 가능한 매니페스트를 생성한다
set -euo pipefail

BUNDLE_DIR="${1:?usage: make-manifest.sh <bundle-dir>}"
RELEASEVER="${2:?usage: make-manifest.sh <bundle-dir> <releasever>}"
SNAPSHOT_DATE="$(date -u +%Y-%m-%d)"

OUT="${BUNDLE_DIR}/MANIFEST.tsv"

{
  echo "# bundle_id: rhel-airgap-${SNAPSHOT_DATE}"
  echo "# snapshot_date: ${SNAPSHOT_DATE}"
  echo "# releasever: ${RELEASEVER}"
  echo "# source_repos: rhel-9-for-x86_64-baseos-rpms,rhel-9-for-x86_64-appstream-rpms"
  echo "# generated_by: $(rpm --version)"
  printf 'NAME\tEPOCH\tVERSION\tRELEASE\tARCH\tSHA256\tFILE\n'
} > "${OUT}"

for f in "${BUNDLE_DIR}"/rpms/*.rpm; do
  # epoch가 없는 패키지는 삼항 연산자로 0을 채워 넣는다
  nevra="$(rpm -qp --queryformat \
    '%{NAME}\t%|EPOCH?{%{EPOCH}}:{0}|\t%{VERSION}\t%{RELEASE}\t%{ARCH}' \
    "$f" 2>/dev/null)"
  sum="$(sha256sum "$f" | cut -d' ' -f1)"
  printf '%s\t%s\t%s\n' "${nevra}" "${sum}" "$(basename "$f")"
done >> "${OUT}"

echo "wrote ${OUT} ($(grep -cv '^#' "${OUT}") lines)"

-p, --package는 rpm man 페이지에서 "Query an (uninstalled) package PACKAGE_FILE"로, --queryformat은 "Output format of each queried package, as described by rpm-queryformat(7)"로 정의됩니다. 삼항 표기는 rpm 질의 형식 문서에 나오는 문법으로, 태그가 없을 때의 값을 지정합니다. epoch가 비어 있는 패키지에 0을 채우는 데 그대로 쓸 수 있습니다.

결과 파일의 모양은 이렇습니다.

# bundle_id: rhel-airgap-2026-08-15
# snapshot_date: 2026-08-15
# releasever: 9.4
# source_repos: rhel-9-for-x86_64-baseos-rpms,rhel-9-for-x86_64-appstream-rpms
# generated_by: RPM version 4.16.1.3
NAME	EPOCH	VERSION	RELEASE	ARCH	SHA256	FILE
httpd	0	2.4.57	11.el9_4	x86_64	3f1c...	httpd-2.4.57-11.el9_4.x86_64.rpm
mod_ssl	1	2.4.57	11.el9_4	x86_64	9ab2...	mod_ssl-2.4.57-11.el9_4.x86_64.rpm

mod_ssl 줄의 epoch가 1인 것을 보세요. 파일 이름 어디에도 그 숫자는 없습니다.

반입 후 대조합니다

매니페스트는 만들어 두는 것으로 끝나지 않습니다. 안쪽에 도착한 직후 대조해야 합니다.

#!/usr/bin/env bash
# verify-bundle.sh — 매니페스트와 실제 번들을 대조한다
set -euo pipefail

BUNDLE_DIR="${1:?usage: verify-bundle.sh <bundle-dir>}"
MANIFEST="${BUNDLE_DIR}/MANIFEST.tsv"
ERR=0

echo "== 번들 메타데이터"
grep '^#' "${MANIFEST}"

echo "== 체크섬 대조"
while IFS=$'\t' read -r name epoch version release arch sha file; do
  path="${BUNDLE_DIR}/rpms/${file}"
  if [ ! -f "${path}" ]; then
    echo "MISSING ${file}"; ERR=$((ERR+1)); continue
  fi
  actual="$(sha256sum "${path}" | cut -d' ' -f1)"
  if [ "${actual}" != "${sha}" ]; then
    echo "MISMATCH ${file}"; ERR=$((ERR+1))
  fi
done < <(grep -v '^#' "${MANIFEST}" | tail -n +2)

echo "== 매니페스트에 없는 파일"
comm -13 \
  <(grep -v '^#' "${MANIFEST}" | tail -n +2 | cut -f7 | sort) \
  <(cd "${BUNDLE_DIR}/rpms" && ls -1 *.rpm | sort)

echo "== 서명 검증"
rpmkeys --checksig "${BUNDLE_DIR}"/rpms/*.rpm \
  | grep -v 'digests signatures OK' || true

echo "== 결과: 오류 ${ERR}건"
exit "${ERR}"

"매니페스트에 없는 파일" 검사를 넣은 이유가 있습니다. 누락은 설치 실패로 금방 드러나지만, 목록에 없는데 매체에 들어 있는 파일은 아무도 모르게 지나갑니다. 반입 심의의 관점에서는 이쪽이 더 큰 문제입니다.

체크섬 파일을 매체에 함께 담는 것에 대해서도 한마디 필요합니다. 같은 매체에만 담긴 체크섬은 매체가 통째로 바뀌면 함께 바뀝니다. 실질적인 보증은 서명에서 나옵니다. 체크섬은 전송 중 손상 검출용이고, 출처 보증은 GPG 서명이 합니다. 그래서 앞의 스크립트가 둘 다 확인합니다.

반입 기록에 무엇을 남기는가

감사 대응과 재현은 같은 기록으로 해결됩니다. 번들 하나당 이 정도를 남기면 충분합니다.

항목예시왜 필요한가
번들 IDrhel-airgap-2026-08-15서버와 번들을 연결하는 키
스냅샷 날짜2026-08-15어느 시점 콘텐츠인지
releasever9.4마이너 버전 고정값 (5편)
출처 저장소 IDrhel-9-for-x86_64-baseos-rpms다시 만들 때의 입력
생성 명령dnf download --installroot=... --releasever=9.4 ...재현의 핵심
매체 해시매체 이미지의 SHA-256매체 단위 무결성
반입 일시·담당자감사 요구
설치 대상 서버영향 범위 추적

"생성 명령"을 통째로 남기라는 항목이 중요합니다. 6개월 뒤 같은 번들을 다시 만들어야 할 때, 옵션 하나가 달라지면 결과가 달라집니다. 사람의 기억 대신 파일에 남겨 두세요. 이 정보를 번들 안 PROVENANCE.txt 같은 파일로 함께 넣으면 매체만으로 자족합니다.

서브스크립션 없이 Red Hat 콘텐츠를 재배포하는 것은 계약 위반일 수 있으니 조직의 라이선스 조건을 먼저 확인하세요. 반입 기록에 출처 저장소 ID를 남겨 두면, 나중에 라이선스 범위를 확인할 때도 그대로 근거가 됩니다.

마치며 — 매체가 자족해야 합니다

이번 편의 원칙은 하나입니다. 매체 하나만 손에 들고도 무엇이 들어 있는지, 어디서 왔는지, 어떻게 다시 만드는지 알 수 있어야 합니다.

rpm 파일만 담긴 USB는 6개월 뒤에 아무것도 말해 주지 않습니다. 여기에 매니페스트와 공개키와 생성 명령을 함께 넣으면, 그 매체는 설명서가 됩니다. 추가 비용은 텍스트 파일 두 개뿐입니다.

명령과 옵션은 2026-08-15에 공식 문서에서 확인했습니다. RHEL 버전에 따라 다르므로 사용 중인 버전의 문서로 다시 확인하세요.

직접 해보기

이전 / 다음 편

참고 자료