/보안/NO_PUBKEY apt 에러 복구 런북 — signed-by keyring 등록법
보안NO_PUBKEYapt-key deprecated

NO_PUBKEY apt 에러 복구 런북 — signed-by keyring 등록법

apt update가 NO_PUBKEY·public key is not available로 막혔나요? 폐지된 apt-key 대신 signed-by keyring 등록과 gpg dearmor로 안전하게 복구하는 진단표와 복붙 런북을 제공합니다.

NO_PUBKEY apt 에러 복구 런북 — signed-by keyring 등록법

NO_PUBKEY apt 에러 30초 복구 런북 — signed-by keyring 등록법

apt update 한 줄에서 멈춘 배포 파이프라인

CI 러너나 신규 서버에서 Docker, Kubernetes, PostgreSQL 저장소를 추가한 뒤 apt update를 돌렸는데 이런 빨간 줄이 뜬 적 있으실 겁니다.

CODE
W: GPG error: https://download.docker.com/linux/ubuntu jammy InRelease:
   The following signatures couldn't be verified because
   the public key is not available: NO_PUBKEY 7EA0A9C3F273FCD8

여기서 가장 흔한 오해는 "네트워크나 방화벽 문제인가?" 하는 반응입니다. 아닙니다. apt는 저장소 메타데이터(InRelease)를 정상적으로 내려받았고, 그 서명을 검증할 공개키가 로컬에 없어서 거부한 것입니다. 즉 NO_PUBKEY는 다운로드 문제가 아니라 신뢰(trust) 문제입니다. 그래서 해결책도 "다시 받기"가 아니라 "그 저장소의 키를 안전하게 등록하기"가 됩니다.

30초 진단표: 에러 원문으로 원인 특정하기

터미널에 뜬 문자열을 아래 표 왼쪽과 매칭해 바로 이동할 경로를 정하세요.

에러 원문 패턴원인이동할 경로
NO_PUBKEY 7EA0A9C3F273FCD8 (키ID가 보임)저장소 서명 키가 로컬에 없음. 공식 키 URL을 안다면 정석 등록경로 A
The following signatures couldn't be verified because the public key is not available위와 동일. 키 미등록 상태경로 A
키 URL은 모르고 NO_PUBKEY <16자리 키ID>만 아는 경우키서버에서 키ID로 회수경로 B
EXPKEYSIG 1234ABCD ... <Signing Key>등록된 키가 만료됨. 우회 금지, 신규 키 재다운로드경로 A (키 갱신)
Warning: apt-key is deprecated... (see apt-key(8))legacy apt-key/trusted.gpg 사용 중. 폐지 예정경로 A로 마이그레이션

복구 경로 A — apt-key를 대체하는 정석: keyrings + signed-by

폐지된 apt-key add 대신, 저장소마다 개별 keyring 파일을 만들고 .sourcessigned-by=로 명시적으로 연결하는 것이 현재의 정석입니다.

Docker

Bash
# 1) keyring 전용 디렉터리 생성(권한 0755)
sudo install -m 0755 -d /etc/apt/keyrings

# 2) 공식 GPG 키를 내려받아 바이너리 keyring으로 변환(dearmor)
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
  | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

# 3) 이 키로만 검증하도록 저장소 정의(signed-by 명시)
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" \
  | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update   # NO_PUBKEY가 사라졌는지 확인

Kubernetes (pkgs.k8s.io — 구 apt.kubernetes.io에서 이전됨)

Bash
# 원하는 마이너 버전(v1.30)에 맞는 Release 키를 dearmor
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key \
  | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] \
https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /" \
  | sudo tee /etc/apt/sources.list.d/kubernetes.list > /dev/null

2023년 apt.kubernetes.io가 폐기되고 pkgs.k8s.io로 이전되면서, 버전별로 키가 분리되었습니다. 이때 구 URL을 그대로 쓰던 다수 서버가 NO_PUBKEY를 겪었습니다.

PostgreSQL (apt.postgresql.org)

Bash
# PGDG 서명 키 등록
curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc \
  | sudo gpg --dearmor -o /etc/apt/keyrings/postgresql.gpg

echo "deb [signed-by=/etc/apt/keyrings/postgresql.gpg] \
https://apt.postgresql.org/pub/repos/apt $(. /etc/os-release && echo $VERSION_CODENAME)-pgdg main" \
  | sudo tee /etc/apt/sources.list.d/pgdg.list > /dev/null

deb822 형식(.sources)을 선호한다면 .list 대신 아래처럼 작성할 수 있습니다.

INI
# /etc/apt/sources.list.d/docker.sources
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: jammy
Components: stable
Signed-By: /etc/apt/keyrings/docker.gpg

복구 경로 B — 키 URL을 모를 때: keyID로 keyserver에서 회수

에러에 뜬 키ID(NO_PUBKEY 7EA0A9C3F273FCD8)만 있고 공식 키 URL을 찾지 못했다면 키서버에서 받습니다. 단, 이 경로는 중간자 위험이 있으니 반드시 지문을 대조하세요.

Bash
# 1) 임시 keyring에 키ID로 키 회수(전역 keyring 오염 방지)
gpg --no-default-keyring --keyring /tmp/tmp.gpg \
  --keyserver keyserver.ubuntu.com \
  --recv-keys 7EA0A9C3F273FCD8

# 2) 지문 확인 — 공식 문서에 공개된 지문과 눈으로 대조(★필수)
gpg --no-default-keyring --keyring /tmp/tmp.gpg --fingerprint 7EA0A9C3F273FCD8

# 3) 지문이 일치하면 export → dearmor로 저장소 keyring 생성
gpg --no-default-keyring --keyring /tmp/tmp.gpg \
  --export 7EA0A9C3F273FCD8 \
  | sudo tee /etc/apt/keyrings/<repo>.gpg > /dev/null

# 4) 임시 keyring 정리
rm /tmp/tmp.gpg

이후 경로 A와 동일하게 .sources/.listsigned-by=/etc/apt/keyrings/<repo>.gpg를 연결하면 됩니다.

⚠️ 보안 주의사항

  • apt-key add가 폐지됐나: legacy 방식은 모든 키를 하나의 전역 keyring(trusted.gpg)에 몰아넣습니다. 그러면 Docker용으로 추가한 키가 PostgreSQL 저장소의 서명까지 검증할 수 있게 됩니다. 키 하나가 탈취되면 신뢰 범위가 시스템 전체로 확대되죠. 저장소별 개별 keyring + signed-by=는 이 신뢰 범위를 한 저장소로 격리합니다.
  • keyserver 경로의 위험: 키서버는 누구나 키를 업로드할 수 있어 동일 키ID로 스푸핑된 키가 올라올 수 있습니다. 반드시 gpg --fingerprint로 공식 문서의 전체 지문과 대조하세요.
  • EXPKEYSIG는 우회 대상이 아니다: 이건 키 만료 신호입니다. 무시하거나 검증을 끄지 말고 신규 키를 재다운로드하는 것이 정답입니다.

하지 말아야 할 것

  • [trusted=yes] — 서명 검증 자체를 꺼버려 변조된 패키지도 그냥 설치합니다.
  • apt-get --allow-unauthenticated — 인증 실패를 강제로 통과시키는, 공급망 공격에 문 열어주는 옵션입니다.
  • /etc/apt/trusted.gpg.d에 무분별 투척 — 다시 전역 신뢰로 회귀하는 것입니다. keyring은 /etc/apt/keyrings에 두고 signed-by로만 연결하세요.

실무 경험 한 줄

여러 서버를 관리하다 보면 Ansible 플레이북에 apt-key add가 남아 있다가 Ubuntu 24.04로 올린 순간 전 서버 배포가 멎는 일을 겪게 됩니다. 저는 아예 롤에서 keyring 등록을 표준화하고, 키 지문을 변수로 박아 gpg --fingerprint 결과와 assert하도록 만들어두니 EXPKEYSIG 갱신도 예측 가능해졌습니다.

재발 방지 체크리스트

  • 전역 apt-key 사용 전면 금지 → /etc/apt/keyrings + signed-by=
  • 저장소마다 개별 keyring 파일 유지(격리 원칙)
  • 키 등록 시 항상 gpg --fingerprint로 공식 지문 대조
  • EXPKEYSIG 대비 키 만료 주기 모니터링 → 만료 전 재다운로드
  • 신규 서버는 .list 대신 deb822 .sources 형식으로 통일

자주 묻는 질문 (FAQ)

Q. apt-key add로 등록하면 지금도 동작하는데 꼭 바꿔야 하나요? A. Ubuntu 22.04부터 deprecated 경고가 뜨고, 24.04 흐름에서는 apt-key가 제거되는 방향입니다. 전역 keyring은 신뢰 확대 위험도 있으니 지금 signed-by 방식으로 마이그레이션하는 것이 안전합니다.

Q. gpg --dearmor는 왜 필요한가요? A. .asc/.gpg 키는 보통 ASCII armored(텍스트) 형식인데, signed-by가 참조하는 keyring은 바이너리여야 합니다. --dearmor가 텍스트 키를 바이너리 keyring으로 변환해 줍니다.

Q. EXPKEYSIG가 떴는데 그냥 무시하고 설치하면 안 되나요? A. 안 됩니다. 만료된 키를 우회하면 공급망 변조를 탐지할 수 없게 됩니다. 정답은 저장소의 최신 키를 다시 내려받아 keyring을 갱신하는 것입니다.

✦ ✦ ✦
편집 검토 · Editorial Review

이 글은 AI 에이전트가 자료 조사와 1차 초안 작성을 담당하고, 사람 편집자가 사실관계·출처·톤과 맥락을 검토한 뒤 발행했습니다. 환경(OS·버전)에 따라 결과가 다를 수 있으니 적용 전 공식 문서를 함께 확인하세요. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.

초안 · AI (Content Reviewer)·검토 · Nodelog 편집자·발행 ·
관련 공식 문서OWASP 공식 문서

댓글

첫 번째 댓글을 남겨보세요.