git tag -s: 태그에 서명 추가하기
git tag -s로 GPG/SSH 서명된 annotated 태그를 생성하는 방법, 서명 태그 오브젝트의 내부 구조, git verify-tag로 서명을 검증하는 방법, 릴리스 워크플로에서 서명 태그를 활용하는 패턴을 설명한다.
지난 글에서 커밋 서명을 다뤘다. 이번에는 릴리스 무결성 보장에 핵심적인 태그 서명을 살펴본다. 서명된 태그는 “이 릴리스는 실제 관리자가 만든 것”임을 암호학적으로 증명한다.
서명 태그 생성
태그에 서명하려면 -s 플래그를 사용한다. 반드시 annotated 태그여야 한다(-a 포함 또는 -s만으로 자동으로 annotated).
# 서명 태그 생성 (에디터에서 메시지 입력)
git tag -s v1.0.0
# 메시지 직접 지정
git tag -s v1.0.0 -m "Release 1.0.0 - stable"
# 특정 커밋에 서명 태그
git tag -s v1.0.0 -m "Release 1.0.0" abc123def456
# 기존 태그 덮어씌기 (이미 push됐으면 주의)
git tag -s v1.0.0 -m "Release 1.0.0" -f
서명 키는 user.signingkey에 설정된 GPG 키(또는 SSH 키)가 사용된다.
tag.gpgsign으로 자동 서명
# 모든 태그에 자동 서명
git config --global tag.gpgsign true
# 이제 -s 없이도 서명됨
git tag -a v1.0.0 -m "Release 1.0.0"
# 특정 태그만 서명 건너뜀
git tag -a v1.0.0 -m "Release" --no-sign
서명된 태그의 내부 구조
서명은 annotated 태그 오브젝트에 포함된다. 커밋 서명의 gpgsig 필드와 달리, 태그 서명은 태그 내용 끝에 PGP 블록이 직접 붙는다.
# 태그 오브젝트 원시 내용 확인
git cat-file -p v1.0.0
# 출력:
# object abc123def456...
# type commit
# tag v1.0.0
# tagger Alice <alice@example.com> 1716868800 +0900
#
# Release 1.0.0 - stable
# -----BEGIN PGP SIGNATURE-----
# iQIzBAABCAAdFiEE...
# -----END PGP SIGNATURE-----
PGP 서명 블록은 태그 내용(object, type, tag, tagger, 메시지)을 대상으로 생성된다. 태그 내용이 바뀌면 서명이 무효가 된다.
서명 검증
# 단일 태그 검증
git verify-tag v1.0.0
# object abc123def456...
# type commit
# tag v1.0.0
# tagger Alice <alice@example.com>
# gpg: Good signature from "Alice <alice@example.com>"
# 태그 상세 정보 + 서명 확인
git tag -v v1.0.0
검증이 실패하면 exit code 1을 반환하므로 스크립트에서 활용할 수 있다.
# 스크립트에서 서명 검증
if git verify-tag v1.0.0 > /dev/null 2>&1; then
echo "Tag v1.0.0 is valid"
else
echo "ERROR: invalid or unsigned tag"
exit 1
fi
릴리스 워크플로에서 서명 태그 활용
# 릴리스 체크리스트 스크립트 예시
VERSION="v1.0.0"
COMMIT=$(git rev-parse HEAD)
# 1) 서명 태그 생성
git tag -s $VERSION -m "Release $VERSION"
# 2) 태그 push
git push origin $VERSION
# 3) 원격에서 fetch 후 검증
git fetch origin $VERSION
git verify-tag $VERSION && echo "Release $VERSION verified"
# 4) 릴리스 아카이브 생성
git archive --format=tar.gz --prefix=${VERSION}/ \
$VERSION > ${VERSION}.tar.gz
lightweight 태그에는 서명 불가
lightweight 태그는 단순히 커밋 SHA를 가리키는 ref 파일이고 별도의 태그 오브젝트가 없다. 서명이 들어갈 공간 자체가 없으므로 서명이 불가능하다.
# lightweight: 서명 없음 (ref 파일만 생성)
git tag v1.0.0-lw
# annotated: 태그 오브젝트 생성 → 서명 가능
git tag -a v1.0.0 -m "Release"
git tag -s v1.0.0 -m "Release" # annotated + 서명
서명이 필요한 릴리스에는 반드시 annotated 태그를 사용해야 한다.
fetch 시 서명 검증
원격에서 태그를 받을 때 서명을 자동으로 검증하는 옵션이 있다.
# fetch 중 서명 검증 (서명 없거나 잘못된 태그면 경고)
git fetch --verify-signatures origin
# merge 시 서명 검증
git merge --verify-signatures feature-branch
--verify-signatures는 merge 대상 커밋이나 태그에 유효한 서명이 없으면 거부한다. 서명이 강제된 팀에서 보안 게이트로 활용할 수 있다.
과거 태그 재서명
이미 push된 unsigned 태그를 소급해서 서명하는 경우, 태그 SHA가 바뀌므로 remote 태그를 삭제하고 재push해야 한다.
# 로컬 태그 삭제 후 재서명
git tag -d v1.0.0-old
git tag -s v1.0.0-old -m "Re-signed release" $COMMIT_SHA
# 원격 태그 삭제 후 재push
git push origin :refs/tags/v1.0.0-old # 원격 삭제
git push origin v1.0.0-old # 새 서명 태그 push
이미 배포된 태그를 바꾸면 기존에 해시를 검증하던 사용자에게 혼란을 줄 수 있으므로 공개 릴리스에서는 신중해야 한다.
지난 글: git commit -S: 커밋에 GPG/SSH 서명 추가하기
읽어주셔서 감사합니다. 😊