빌드는 초록불이었는데 그 라이브러리는 누가 넣었나 · 증거가 없으면 배포하지 않는다 · 이론
증거가 없으면 배포하지 않는다
한 줄 요약
목록과 서명과 증명은 배포 직전에 읽히지 않으면 없는 것과 같고, 관문의 값어치는
무엇을 막았는가가 아니라 왜 막았는지를 사람이 읽을 수 있는가에서 나옵니다.
왜 이게 필요했나
앞의 세 모듈에서 만든 것은 전부 증거입니다. 무엇이 들어 있는지 적은 목록, 이 바이트가
우리 것이라는 서명, 어디서 나왔는지 적은 증명. 그런데 증거는 법정에 제출되지 않으면
아무 일도 하지 않습니다. 배포 직전에 그것을 열어 보고 모자라면 멈추는 절차가 관문입니다.
관문을 만들 때 사람들이 가장 자주 하는 실수는 서명만 보는 것입니다. 서명 검증은
"이 바이트가 이 키로 서명됐다" 까지만 말합니다. 그 키가 우리 것이고, 그 서명이 붙은
증명이 진짜 우리 빌더에서 나왔고, 증명이 가리키는 대상이 지금 배포하려는 바로 그 파일인지는
각각 따로 물어야 하는 질문입니다. 셋 중 하나만 빠져도 통과하는 경로가 생깁니다.
어떻게 동작하나
관문이 던지는 질문은 네 가지로 정리됩니다.
목록이 붙어 있는가 SBOM 이 있고 비어 있지 않은가서명이 맞는가 우리가 믿는 키로 검증되는가대상이 같은가 증명의 subject 다이제스트가 지금 이 파일의 해시와 같은가빌더를 허락했는가 builder.id 가 허용 목록에 있는가네 번째 줄이 자주 빠지는데, 실무에서 가장 조용히 뚫리는 곳이 거기입니다. 서명도 맞고
다이제스트도 맞는데 그 빌드가 누군가의 노트북에서 나온 경우, 앞의 셋만 보는 관문은
그대로 통과시킵니다. SLSA 가 Build L2 에서 전용 기반 시설 위의 호스팅 플랫폼을
요구하는 이유가 여기 있습니다 — 어디서 빌드했는지가 보안 속성 자체이기 때문입니다
([SLSA 보안 레벨](https://slsa.dev/spec/v1.0/levels)). cosign 의 검증도 같은 방향을
봅니다. 문서는 cosign verify 가 서명뿐 아니라 **서명 인증서가 신뢰하는 인증 기관에서
나왔는지**까지 확인한다고 적습니다
([cosign 검증](https://docs.sigstore.dev/cosign/verifying/verify/)).
또 하나 중요한 설계 선택은 사유를 코드로 남기는 것입니다. 실패 메시지를 문장으로만
내면 사람마다 다르게 적고, 그러면 "지난달에 무엇 때문에 가장 많이 막혔나" 를 셀 수
없습니다. signature_missing 이나 builder_not_allowed 처럼 짧은 코드로 남기면 판정이
그대로 통계가 되고, 통계가 있으면 다음에 무엇을 고쳐야 하는지가 논쟁이 아니라 숫자로
정해집니다.
마지막으로 면제의 자리를 관문 안에 만들어 두어야 합니다. 예외가 없는 관문은 급할 때
통째로 꺼지고, 한 번 꺼진 관문은 다시 켜지지 않습니다. 예외를 기록으로 남기게 만들면
관문은 켜진 채로 남습니다.
현장에서 만나는 모습
가장 흔한 실패는 관문이 경고만 내고 멈추지 않는 것입니다. 파이프라인 로그에 노란
줄이 남고 배포는 그대로 진행됩니다. 처음에는 "익숙해질 때까지" 라는 이유로 시작하고,
그다음에는 아무도 그 줄을 읽지 않습니다. 멈추지 않는 관문은 관문이 아닙니다.
두 번째는 통과하는 경우를 먼저 확인하지 않는 것입니다. 막는 것부터 시험하면 '항상
막는' 관문도 성공한 것처럼 보입니다. 정상 릴리스가 통과하는 것을 먼저 못박고, 그다음
막혀야 하는 경우를 하나씩 만들어야 양방향이 모두 확인됩니다.
세 번째는 판정을 남기지 않는 것입니다. 관문이 막았다는 사실은 그 순간 화면에만 있고,
몇 주 뒤 "그때 왜 막혔지" 를 물으면 아무도 답하지 못합니다. 판정과 근거, 대상의
다이제스트를 한 줄로 적어 두면 그 질문이 답할 수 있는 질문이 됩니다.
네 번째는 관문을 너무 늦게 두는 것입니다. 배포 순간에만 검사하면 그때는 이미 릴리스가
예정돼 있고 사람들이 모여 있어서, 막히는 순간 관문을 끄자는 말이 나옵니다. 같은 검사를
빌드 직후에도 한 번 돌리면 문제가 몇 시간 전에 드러나고, 배포 직전의 검사는 확인에
가까워집니다. 같은 프로그램을 두 자리에서 부르는 것뿐이라 비용도 거의 들지 않습니다.
다섯 번째는 관문이 보는 증거를 관문 자신이 만드는 것입니다. 배포 직전에 SBOM 을 다시
만들어 검사하면, 그 목록은 빌드가 실제로 쓴 것이 아니라 지금 다시 푼 것입니다. 둘이
어긋나는 날이 바로 확인하고 싶었던 그날인데, 그 어긋남이 보이지 않게 됩니다. 증거는
만든 쪽이 붙이고 관문은 읽기만 해야 합니다.
여섯 번째는 관문을 켜는 순서입니다. 처음부터 네 검사를 한꺼번에 강제로 돌리면 기존
릴리스가 전부 막혀서, 그날 관문이 꺼집니다. 실무에서 살아남는 순서는 사유만 기록하는
기간을 짧게 두고, 어떤 사유가 몇 건 나오는지 숫자로 본 뒤, 하나씩 차례로 강제로 바꾸는
것입니다. 이때도 "언제까지 전부 강제로 바꾼다" 는 날짜를 함께 못박아야 합니다 — 날짜가
없으면 기록만 하는 기간이 영원히 끝나지 않습니다.
다음 실습에서 할 것
릴리스 묶음에 출처 증명을 붙여 서명하고, 네 가지를 확인해 사유 코드와 함께 막는 관문을
만듭니다. 정상 묶음이 통과하는 것을 먼저 확인한 뒤, 서명이 없는 묶음·산출물이 바뀐
묶음·서명은 맞지만 허락하지 않은 빌더가 만든 묶음 셋을 직접 만들어 각각 다른 사유로
막히는 것까지 확인하고, 그 판정을 감사 기록으로 남깁니다.