LabHub
배우기 러닝패스 코스

빌드는 초록불이었는데 그 라이브러리는 누가 넣었나 · 이 바이트가 어디서 왔는지 아무도 모른다 · 이론

이 바이트가 어디서 왔는지 아무도 모른다

LabHub 에서 이어서 보기

한 줄 요약

출처 증명(provenance)은 "무엇이, 무엇으로부터, 누구에 의해, 언제 만들어졌는가" 를
빌드 시점에 적어 두는 일이고, SLSA 는 그 기록을 얼마나 믿을 수 있는지를 단계로 나눕니다.

왜 이게 필요했나

목록과 서명이 다 있어도 답이 안 나오는 질문이 하나 남습니다. "이 바이트가 어디서
왔는가." 서명은 "누가 이걸 서명했나" 에 답하고 SBOM 은 "무엇이 들어 있나" 에 답하지만,
둘 다 그 산출물이 어느 소스에서 어떤 절차로 나왔는지는 말해 주지 않습니다.

이 질문이 실제로 문제가 되는 장면은 극적이지 않습니다. 대개는 이렇습니다 — 급해서
누군가 자기 노트북에서 빌드해 올렸고, 그 산출물에도 회사 키로 서명이 붙어 있습니다.
서명은 통과합니다. 목록도 붙어 있습니다. 그런데 그 빌드가 어느 커밋에서 나왔는지,
어떤 의존을 썼는지 아무도 모릅니다. 몇 달 뒤 문제가 생겼을 때 되짚을 수 있는 실이
한 가닥도 없습니다.

어떻게 동작하나

SLSA 는 이것을 트랙과 레벨로 나눕니다. Build 트랙에서 L0 은 아무 보장이 없는
상태이고, L1 은 산출물이 어떻게 만들어졌는지를 적은 출처 증명이 존재하는 것입니다.
L1 의 증명은 불완전하거나 서명되지 않았을 수 있어서 실수는 막지만 위조는 막지 못합니다.
L2 는 전용 기반 시설 위의 호스팅 빌드 플랫폼이 증명을 만들고 서명하는 것이고, 그래서
빌드 이후의 변조를 막습니다. L3 은 빌드 플랫폼 자체를 굳혀 실행끼리 서로 영향을 주지
못하게 하고 증명 서명에 쓰는 비밀을 사용자 정의 빌드 단계가 만질 수 없게 합니다
([SLSA 보안 레벨](https://slsa.dev/spec/v1.0/levels)). 이 레벨 문서는 현재 v1.0 판이
'Retired' 로 표시돼 있고 더 새 판을 가리키고 있습니다 — 인용할 때 확인해야 할 대목입니다.

증명의 모양은 in-toto Statement 를 씁니다. 맨 위에 _typesubject 가 있고,
predicateType 으로 술어의 종류를 가리킵니다. SLSA 문서는 여기에 **URL 창에 보이는
주소가 아니라 https://slsa.dev/provenance/v1 을 그대로 넣으라고 못박아** 두었습니다.
술어 안쪽은 둘로 나뉩니다.

buildDefinition   무엇을 만들라고 했는가  buildType            이 칸들을 어떻게 읽어야 하는지 가리키는 URI  externalParameters   빌드에 밖에서 넣은 값 (소스 주소와 커밋, 진입점 등)  internalParameters   플랫폼이 스스로 채운 값  resolvedDependencies 실제로 쓴 재료와 그 다이제스트runDetails        누가 언제 실행했는가  builder.id           이 빌드를 수행한 플랫폼의 신원  metadata             invocationId · startedOn · finishedOn

문서가 Build L1 의 필수로 적는 것은 buildDefinitionrunDetails, 그 안에서
buildTypeexternalParameters, 그리고 builder 입니다
([SLSA Provenance v1](https://slsa.dev/spec/v1.0/provenance)). 나머지는 있으면 좋은
칸입니다. 특히 builder.id신뢰의 경계를 통째로 짊어지는 칸이라는 점을 문서가
따로 설명합니다 — 그 식별자가 가리키는 플랫폼을 믿는다는 선언이기 때문입니다.

resolvedDependencies 의 값어치는 재료마다 다이제스트를 함께 적는 데 있습니다.
이름만 적으면 "같은 입력으로 다시 만들면 같은 것이 나오는가" 를 물을 수 없습니다.
다이제스트가 있으면 그 질문을 나중에, 사고가 난 다음에도 던질 수 있습니다.

현장에서 만나는 모습

첫 번째 함정은 증명을 만들지만 아무도 읽지 않는 것입니다. 파이프라인이 JSON 을
하나 더 뱉고 그것을 아티팩트 저장소에 올리는 데서 끝납니다. 배포 직전에 그 파일을 열어
보는 절차가 없으면, 그 증명은 존재하는 것만으로는 아무것도 바꾸지 않습니다.

두 번째는 externalParameters 에 빌드 환경 전체를 통째로 붓는 것입니다. 문서는 이 칸을
최소로 유지하라고 권합니다. 값이 많아질수록 검증하는 쪽이 "무엇이 정상인지" 를 정의
하기 어려워지고, 결국 아무도 비교하지 않는 큰 덩어리가 됩니다.

세 번째는 커밋 해시 대신 브랜치 이름을 적는 것입니다. 브랜치는 움직이는 포인터라
같은 증명이 시점마다 다른 소스를 가리키게 됩니다. 다이제스트로 못박아야 합니다.

네 번째는 증명을 만드는 주체를 헷갈리는 것입니다. 증명은 빌드를 수행한 쪽이 만들어야
값어치가 있습니다. 빌드가 끝난 뒤 사람이 손으로 채운 증명은 그 사람이 아는 것만 적히고,
그 사람이 속으면 증명도 함께 속습니다. SLSA 가 레벨을 올릴수록 '누가 증명을 만드는가' 를
좁혀 가는 이유가 이것입니다 — L2 에서 호스팅 플랫폼이 만들고 서명하게 하고, L3 에서는
그 서명 비밀을 사용자 빌드 단계가 만질 수 없게 합니다.

다음 퀴즈에서 확인할 것

Build 트랙의 각 레벨이 무엇을 막는지, 증명의 어느 칸이 Build L1 의 필수인지,
그리고 builder.id 가 왜 신뢰의 경계를 짊어지는 칸인지 확인합니다. 이어지는 모듈에서는
이 증명을 실제로 만들어 서명하고, 그것을 읽는 관문까지 세웁니다.