회사 출입 규정이 적힌 책이 있다고 생각해 봅시다. 이 책에는 “누가 어느 문을 열 수 있는가”, “어떤 경우에 출입을 거부해야 하는가” 같은 규칙이 들어 있습니다. 누군가 몰래 한 줄을 고쳐도 겉표지는 그대로입니다. 경비원이 바뀐 사실을 모르고 책을 사용하면 출입 통제 전체가 달라질 수 있습니다.

그래서 규정집을 승인한 사람이 마지막에 특별한 봉인을 붙입니다. 경비원은 책을 사용하기 전에 봉인이 올바른지 확인합니다. 봉인이 깨졌거나 승인하지 않은 사람이 만든 봉인이라면 그 책을 사용하지 않습니다.

Wyrelog의 custodian 서명(custodian signature)도 이와 비슷합니다. Wyrelog은 접근 제어 규칙이 담긴 Datalog 템플릿을 읽기 전에 디지털 서명을 확인합니다. 다만 먼저 분명히 할 점이 있습니다.

Custodian 서명은 새로운 암호 알고리즘의 이름이 아니다. 릴리스 산출물과 서명키를 관리하는 책임자, 즉 release custodian이 만든 디지털 서명을 역할 중심으로 부르는 말이다.

custodian은 원래 맡겨진 것을 지키고 돌보는 사람이라는 뜻입니다. 한국어로는 문맥에 따라 관리인, 보관 책임자 정도로 옮길 수 있습니다. 역할의 초점으로 나누면 owner는 자산의 목적과 책임을 정하고, operator는 시스템을 실제로 운용하며, custodian은 맡겨진 자산을 안전하게 보관하고 보호합니다. 한 사람이 여러 역할을 함께 맡을 수도 있지만 각 역할이 강조하는 책임은 다릅니다.

Wyrelog에서 custodian에게 맡겨진 핵심 자산은 오프라인 Ed25519 개인 서명키입니다. Release custodian은 이 키를 보호하고 릴리스 서명 절차를 관리합니다. 따라서 custodian은 암호 기술의 이름도 아니고, 반드시 운영 서버의 operator를 뜻하는 것도 아닙니다.

Wyrelog이 실제로 사용하는 서명 알고리즘은 Ed25519입니다. 이 글에서는 custodian 서명이 무엇인지부터 시작해, Wyrelog에 왜 필요한지와 코드 안에서 어떻게 검증되는지를 차례로 살펴보겠습니다.

봉인된 규정집으로 이해하는 디지털 서명

디지털 서명에는 세 가지 재료가 등장합니다.

규정집 비유 디지털 서명
규정집의 내용으로 만든 고유한 지문 SHA-256 해시
custodian만 가지고 있는 봉인 도구 개인키(private key)
누구나 봉인을 검사할 수 있는 도구 공개키(public key)
봉인 방식 Ed25519

SHA-256은 긴 문서를 32바이트짜리 지문으로 줄입니다. 문서에서 한 글자만 달라져도 전혀 다른 지문이 나옵니다. 하지만 해시만 기록해서는 충분하지 않습니다. 공격자가 문서를 바꿀 수 있다면 새 문서의 해시도 다시 계산해 함께 바꿀 수 있기 때문입니다.

디지털 서명은 이 지문에 개인키를 연결합니다. custodian은 외부에 공개하지 않는 개인키로 지문에 서명하고, 운영 서버는 공개키로 서명이 맞는지 확인합니다. 공격자가 문서와 해시를 모두 바꾸더라도 개인키가 없으면 유효한 새 서명을 만들 수 없습니다.

여기서 서명과 암호화는 구분해야 합니다.

  • 암호화는 허가받지 않은 사람이 내용을 읽지 못하게 합니다.
  • 디지털 서명은 내용이 바뀌지 않았는지, 특정 키로 서명되었는지를 확인합니다.

Wyrelog의 정책 템플릿은 비밀 문서가 아닙니다. 필요한 것은 기밀성이 아니라 정해진 방식으로 정규화한 내용이 승인된 템플릿과 같다는 무결성입니다.

Wyrelog의 정책 파일은 왜 봉인이 필요한가

Wyrelog의 templates/access 아래에는 접근 제어에 쓰이는 Datalog 프로그램이 있습니다. 이 파일들은 단순한 설명서가 아닙니다. 주체의 상태, 세션 상태, 권한 범위와 허용·거부 결정을 실제로 계산하는 실행되는 정책 코드입니다.

예를 들어 허용 조건의 부정 하나가 사라지거나 relation 이름이 바뀌면 같은 요청에 대한 결정이 달라질 수 있습니다. 패키징 실수로 파일 일부가 누락되거나, 배포 과정에서 예전 파일과 새 파일이 섞여도 문제가 됩니다. 마이그레이션 파일이 바뀌면 정책 변경의 적용 순서와 허용되는 변경 방식도 달라질 수 있습니다.

따라서 Wyrelog은 다음 질문에 답할 수 있어야 합니다.

  1. 지금 읽은 정책 템플릿의 canonical 지문이 릴리스 때 만든 것과 같은가?
  2. 이 템플릿의 지문은 릴리스 custodian이 관리하는 키로 서명되었는가?
  3. 마이그레이션이 중간에 빠지거나 순서가 바뀌지 않았는가?
  4. 이 산출물이 운영자가 배포하기로 승인한 정확한 릴리스인가?

첫 번째와 두 번째 질문은 해시와 Ed25519 서명이 담당합니다. 세 번째는 서명과 별도의 마이그레이션 규칙 검사가 함께 담당합니다. 네 번째는 서명 하나만으로 해결되지 않으며, 뒤에서 볼 외부 릴리스 정보와 배포 절차가 필요합니다.

개인키를 운영 서버에 두지 않는 이유

Wyrelog의 운영 문서는 개인 Ed25519 서명키를 release custodian 역할이 소유하고, 배포된 Wyrelog 호스트 밖에 보관하도록 정합니다. 운영 서버에는 서명된 산출물과 검증용 공개키만 전달합니다.

태그된 릴리스 커밋에서 canonical SHA-256을 계산하고 release custodian이 오프라인 개인키로 서명한 뒤, 개인키는 보관 영역에 남겨 두고 서명된 패키지와 공개키만 운영 서버로 보내 검증 후 로드하는 흐름

개인키가 운영 서버에 있으면 서버를 침해한 공격자가 정책을 바꾼 뒤 그 자리에서 다시 서명할 수 있습니다. 개인키를 배포 환경과 분리하면 서버를 운영하는 권한과 새 정책 릴리스를 승인하는 권한도 분리됩니다. 운영 서버는 서명을 검증할 수 있지만 새 서명을 발급할 수는 없습니다.

이 구조는 우발적인 파일 손상, 잘못된 패키징, 배포 중 변조, 승인되지 않은 템플릿 교체를 발견하는 데 유용합니다.

Wyrelog은 무엇에 서명하는가

Wyrelog은 템플릿 파일을 각각 따로 서명하지 않습니다. wyl-engine.c가 다음 다섯 파일을 고정된 순서로 읽습니다.

  1. bootstrap.dl
  2. fsm/principal.dl
  3. fsm/session.dl
  4. fsm/permission_scope.dl
  5. lobac/decision.dl

파일 사이에는 줄바꿈 문자 \n 하나를 넣어 하나의 바이트열로 합칩니다. 그 뒤 \r 문자를 제외하면서 SHA-256을 계산합니다. Windows의 CRLF 체크아웃과 Unix의 LF 체크아웃이 같은 템플릿 정체성을 갖게 하려는 처리입니다. 모든 공백을 마음대로 정규화하는 것은 아니며, 제외되는 것은 \r입니다. 파일 순서와 사이에 삽입되는 \n도 해시 계산 규칙에 포함됩니다.

서명 대상은 템플릿 원문 전체가 아니라 다음 두 값을 이어 붙인 바이트열입니다.

"wyrelog-template-v0-sha256"
+ 템플릿의 32바이트 SHA-256

앞의 문자열은 서명 context입니다. 같은 키와 같은 해시가 다른 종류의 산출물에서 재사용되더라도, 그것을 Wyrelog 템플릿 서명으로 착각하지 않게 용도를 구분합니다. 봉인에 단순히 도장만 찍는 대신 “정책 템플릿 승인용”이라고 함께 새기는 셈입니다.

계산 결과와 서명 정보는 manifest.ini에 들어 있습니다.

[template]
version=0
migration_semantics=append-only
migration_path=migrations
sha256=...

[signature]
algorithm=ed25519
context=wyrelog-template-v0-sha256
public_key=...
ed25519=...
필드 역할
version 기본 템플릿 버전
migration_semantics 마이그레이션이 과거 의미를 지우지 않는 append-only 방식임을 선언
migration_path 마이그레이션 산출물이 있는 상대 경로
sha256 고정 순서로 결합한 템플릿의 예상 지문
public_key Ed25519 서명을 검사할 공개키
ed25519 custodian 개인키로 만든 서명

algorithmcontext도 manifest에 사람이 읽을 수 있는 서명 정보로 기록됩니다. 다만 현재 런타임이 이 값을 읽어 알고리즘과 context를 동적으로 고르는 것은 아닙니다. 실제 검증은 C 코드에 고정된 Ed25519와 wyrelog-template-v0-sha256을 사용합니다. manifest 구조 자체가 올바른지는 별도의 템플릿 트리 테스트가 확인합니다.

정책을 로드하기 전에 어떻게 검증하는가

엔진의 검증 순서는 다음과 같습니다.

다섯 정책 템플릿을 고정 순서로 결합하고 CR을 제외해 SHA-256을 계산한 뒤 manifest hash, context-bound Ed25519 서명, migration chain을 차례로 검증하며 어느 단계든 실패하면 Datalog 정책을 로드하지 않고 거부하는 fail-closed 흐름

템플릿 해시가 다르면 정책 오류로, 서명이 맞지 않으면 암호 오류로 처리합니다. 어느 경우든 Datalog 정책 프로그램을 실행 엔진에 넘기기 전에 중단합니다. 검증 실패 뒤에도 일단 실행해 보는 경로가 없는 fail-closed 방식입니다.

개발 빌드는 manifest가 아예 없을 때 이를 허용할 수 있습니다. 그러나 manifest가 존재하면 검증하며, --production으로 시작한 daemon은 manifest를 필수로 요구합니다. 빌드할 때 require_template_manifest 옵션을 켜면 라이브러리의 기본 템플릿 로드 경로에서도 manifest를 강제할 수 있습니다.

마이그레이션도 별도로 서명한다

기본 템플릿과 마이그레이션은 같은 것이 아닙니다. 기본 템플릿이 현재 규정집 본문이라면, 마이그레이션은 “규정집이 어떤 순서와 제약으로 바뀔 수 있는가”를 기록한 변경 장부에 가깝습니다.

예를 들어 현재의 0000-baseline.ini는 다음 필드를 가집니다.

id
sequence
from_version
to_version
semantics
operation
reserved_namespace

Wyrelog은 INI 파일 전체를 그대로 해시하지 않습니다. 이 일곱 필드를 정해진 줄 형식으로 직렬화한 canonical payload를 만들고 SHA-256을 계산합니다. 마이그레이션 서명은 템플릿과 다른 context를 사용합니다.

"wyrelog-template-migration-v0-sha256"
+ canonical migration payload의 32바이트 SHA-256

context를 나누었기 때문에 정상적인 템플릿 서명을 마이그레이션 서명 자리에 옮겨 쓸 수 없습니다.

서명이 유효하다고 모든 마이그레이션 규칙을 자동으로 만족하는 것은 아닙니다. 엔진은 암호 검증과 함께 다음 정책 조건을 따로 검사합니다.

  • .ini 파일 이름을 정렬했을 때 sequence가 0부터 끊김 없이 이어져야 합니다.
  • 파일명의 0000- 같은 접두사와 sequence가 일치해야 합니다.
  • idsequence가 중복되면 안 됩니다.
  • 앞 단계의 to_version과 다음 단계의 from_version이 이어져야 합니다.
  • semanticsappend-only여야 합니다.
  • operationbaseline, additive, supersession 중 하나여야 합니다.
  • 예약 namespace는 wr.이어야 합니다.
  • 마이그레이션 경로는 상대 경로여야 하며 ..을 포함할 수 없습니다.

즉 서명은 “이 canonical payload가 이 키로 서명되었다”를 증명하고, 별도의 정책 검사는 “그 payload가 Wyrelog의 append-only 진화 규칙에 맞는다”를 확인합니다.

릴리스와 운영에서는 어떻게 사용하는가

Wyrelog의 operator-runbook.md는 서명을 단순한 코드 기능이 아니라 릴리스 절차의 일부로 다룹니다.

릴리스 custodian은 태그된 릴리스 커밋에서 패키지를 만들고 다음 작업을 수행합니다.

  1. 고정된 로드 순서로 canonical template digest를 만듭니다.
  2. wyrelog-template-v0-sha256 context로 템플릿 digest에 서명합니다.
  3. 각 마이그레이션 digest에는 wyrelog-template-migration-v0-sha256 context로 서명합니다.
  4. 템플릿 버전, SHA-256, 마이그레이션 수, 최신 마이그레이션 버전, 서명 공개키 fingerprint를 릴리스 노트에 기록합니다.
  5. 서명된 패키지와 공개키를 운영 호스트에 배포합니다. 개인키는 보내지 않습니다.

운영자는 다음 명령으로 설치된 템플릿의 정체성을 확인할 수 있습니다.

wyrelogd --template-info --template-dir /usr/share/wyrelog/access

이 명령은 manifest와 마이그레이션을 검증한 뒤 version, sha256, 마이그레이션 수, 최신 마이그레이션 버전을 출력합니다. verify-template-release.sh는 이 네 값을 배포 대상으로 승인한 릴리스 값과 비교합니다.

/usr/share/wyrelog/tools/verify-template-release.sh \
  /usr/bin/wyrelogd /usr/share/wyrelog/access \
  EXPECTED_VERSION EXPECTED_SHA256 \
  EXPECTED_MIGRATIONS EXPECTED_LATEST_MIGRATION_VERSION

예전 산출물도 과거에는 유효한 서명을 받았을 수 있습니다. 따라서 서명 검증만으로 downgrade나 replay를 막을 수는 없습니다. 운영자는 위 명령으로 이번 배포에 승인된 정확한 릴리스 정체성과 대조합니다. Wyrelog의 운영 정책은 제자리 downgrade를 지원하지 않으며, rollback이 필요하면 패키지, 템플릿, 정책 저장소, 감사 저장소와 KeyProvider 상태를 하나의 일관된 백업으로 복원하도록 합니다. 잘못된 산출물이 발견되면 과거 파일을 덮어쓰지 않고, 이를 대체하는 새 릴리스를 발행합니다.

서명키 교체도 릴리스 이벤트입니다. 키가 유출되었다고 의심되면 rollout을 멈추고, 새 오프라인 개인키로 서명한 대체 릴리스를 발행하며, 문제가 된 산출물 정체성을 배포 자동화에서 거부해야 합니다.

서명만으로 해결되지 않는 마지막 질문

여기에는 중요한 신뢰 경계가 하나 있습니다. 현재 manifest.ini와 각 마이그레이션 파일에는 서명과 공개키가 함께 들어 있습니다. 산출물 전체를 바꿀 수 있는 공격자라면 정책과 해시뿐 아니라 자신의 공개키와 서명도 함께 넣을 수 있습니다.

그렇다면 런타임의 서명 확인은 무엇을 보장할까요?

질문 확인 수단
파일 내용이 manifest의 지문과 같은가? SHA-256 비교
이 서명이 함께 들어 있는 공개키로 검증되는가? Ed25519 검증
이 공개키와 산출물이 조직이 승인한 custodian의 릴리스인가? 외부의 승인된 릴리스 정보와 배포 절차

세 번째 질문에는 산출물 바깥의 신뢰 기준이 필요합니다. Wyrelog runbook은 릴리스 노트에 공개키 fingerprint와 템플릿 정체성을 기록하도록 하고, 운영자는 승인된 버전과 SHA-256 등의 값을 설치 결과와 대조합니다.

현재 verify-template-release.sh가 직접 비교하는 값은 version, 템플릿 sha256, 마이그레이션 수, 최신 마이그레이션 버전 네 가지입니다. 공개키 fingerprint 자체를 출력하거나 비교하지는 않습니다. 따라서 “내장된 공개키로 서명이 맞았다”를 곧바로 “조직의 custodian이 승인했다”로 확대해서는 안 됩니다. 공개키 fingerprint 검토와 승인값의 안전한 전달은 릴리스·배포 체계가 책임져야 합니다.

이 구분은 custodian 서명의 가치를 낮추는 것이 아닙니다. 오히려 각 장치가 맡는 일을 정확히 나눕니다.

  • 해시는 바이트의 정체성을 고정합니다.
  • Ed25519 서명은 그 정체성과 서명키를 묶습니다.
  • 오프라인 키 관리는 운영 서버 침해와 서명 권한을 분리합니다.
  • 외부 릴리스 승인은 어떤 키와 어떤 산출물을 신뢰할지 결정합니다.
  • 마이그레이션 검사는 정책 변화가 허용된 형태인지 확인합니다.

정책 파일도 코드다

Wyrelog에서 custodian 서명이 필요한 이유는 정책 파일도 코드이기 때문입니다. 접근 허용과 거부를 결정하는 Datalog 템플릿은 실행 파일만큼 엄격하게 배포되어야 합니다.

Custodian 서명은 모든 공급망 문제를 해결하는 마법의 봉인이 아닙니다. 그러나 승인된 정책 묶음의 지문을 오프라인 서명키와 연결하고, 운영 서버가 실행 전에 이를 확인하게 합니다. 여기에 외부 릴리스 승인값과 append-only 마이그레이션 검사까지 더하면, Wyrelog은 “파일이 있으니 일단 실행한다”가 아니라 “누가 승인한 어떤 정책인지 확인한 뒤 실행한다”는 경계를 세울 수 있습니다.

더 읽을 자료