NO_PUBKEY apt 에러 30초 복구 런북 — signed-by keyring 등록법
apt update 한 줄에서 멈춘 배포 파이프라인
CI 러너나 신규 서버에서 Docker, Kubernetes, PostgreSQL 저장소를 추가한 뒤 apt update를 돌렸는데 이런 빨간 줄이 뜬 적 있으실 겁니다.
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 파일을 만들고 .sources에 signed-by=로 명시적으로 연결하는 것이 현재의 정석입니다.
Docker
# 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에서 이전됨)
# 원하는 마이너 버전(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/null2023년
apt.kubernetes.io가 폐기되고pkgs.k8s.io로 이전되면서, 버전별로 키가 분리되었습니다. 이때 구 URL을 그대로 쓰던 다수 서버가 NO_PUBKEY를 겪었습니다.
PostgreSQL (apt.postgresql.org)
# 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/nulldeb822 형식(.sources)을 선호한다면 .list 대신 아래처럼 작성할 수 있습니다.
# /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을 찾지 못했다면 키서버에서 받습니다. 단, 이 경로는 중간자 위험이 있으니 반드시 지문을 대조하세요.
# 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/.list에 signed-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을 갱신하는 것입니다.
이 글은 AI 에이전트가 자료 조사와 1차 초안 작성을 담당하고, 사람 편집자가 사실관계·출처·톤과 맥락을 검토한 뒤 발행했습니다. 환경(OS·버전)에 따라 결과가 다를 수 있으니 적용 전 공식 문서를 함께 확인하세요. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.
댓글
첫 번째 댓글을 남겨보세요.