감사인이 이름을 대며 요구하는 21-compliance.md 14절의 통제들입니다. 이 페이지는 절차이며, 그 절차가 만들어내는 기록이 증거입니다.
키
저장된 모든 자격 증명은 새로운 데이터 암호화 키(DEK)로 봉인됩니다. DEK는
마스터 키(KEK)로 감싸이며, 마스터 키의 참조는 그 옆에 저장됩니다(key_ref,
또는 패킹된 값 안의 참조). 마스터 키를 로테이션하면 DEK를 다시 감쌉니다.
자격 증명을 복호화하거나 다시 암호화하는 일은 결코 없습니다.
| 설정 | 의미 |
|---|---|
QUIRE_MASTER_KEY |
현재 마스터 키: 32바이트, base64. 모든 새 시크릿은 이것으로 감싸입니다 |
QUIRE_MASTER_KEY_VERSION |
그 버전 라벨. 미설정 시 v1. 키를 바꿀 때마다 올리세요 |
QUIRE_MASTER_KEY_RETIRED |
시크릿을 저장했던 이전 키들. 여전히 아래에 있을 수 있습니다. 형식은 v1=<base64>,v0=<base64>. 읽기 전용, 쓰지 않습니다 |
웹 계층, 워커, bun run kek:rotate 명령은 같은 세 설정을 읽습니다. 값이
같아야 하며, 하나라도 다르면 한쪽이 봉인한 것을 다른 쪽이 열 수 없습니다.
QUIRE_MASTER_KEY가 없으면 각 하위 시스템은 QUIRE_SECRET_KEY에서 파생한
키를 유지합니다. 이것은 동작하지만 시스템 상태 페이지에는 성능 저하로
표시되고, 마스터 키를 설정한 뒤에도 읽을 수 있는 상태로 남습니다. 바로
그렇게 첫 로테이션이 모든 것을 그곳에서 옮기는 것입니다. 프로세스 환경을
읽을 수 있는 사람은 누구든 저장된 모든 자격 증명을 복호화할 수 있으므로,
프로덕션 설치에는 마스터 키가 있어야 하며, 시크릿 저장소에 두고 데이터베이스와
같은 백업에 두지 마세요.
로테이션
키 수명은 플랫폼 콘솔의 Security, Master key와 지표
quire.secrets.master_key.age(일 단위)에 표시됩니다. 매일의
platform.key_age 스케줄(03:41 UTC)은 키가 365일에 이르면 플랫폼 감사
체인에 알림 항목을 쓰고, 로테이션될 때까지 30일마다 다시 씁니다. 알림을
받을 때, 그리고 키가 노출되었을 수 있는 언제라도 로테이션하세요.
- 새 키를 만드세요:
openssl rand -base64 32. QUIRE_MASTER_KEY에 그 키를,QUIRE_MASTER_KEY_VERSION에 다음 라벨 (v2)을 설정하세요. 이전 키는QUIRE_MASTER_KEY_RETIRED로v1=<old base64>형식으로 옮기세요. 둘 다의 사본을 이 호스트가 아닌 어딘가에 보관하세요.- 새 설정으로 웹 계층과 워커를 배포하세요. 이제 새 시크릿은
env:QUIRE_MASTER_KEY:v2아래로 감싸이고, 이전 시크릿은 은퇴한 키로 여전히 열립니다. - 감사 추적에 남을 사유와 함께 로테이션을 요청하세요.
- 콘솔에서: Security, Master key, Rotate the master key; 또는
- 같은 환경의 셸에서:
bun run kek:rotate request --reason "Annual rotation, ticket SEC-114".
- 워커는 분당 한 조각씩 다시 감싸고(스케줄러의
platform.key_rotation스케줄), 재시작 후에도 이어집니다. 한 번에 끝내려면:bun run kek:rotate run. 진행 상황은bun run kek:rotate status로 지켜보세요. - 기록에 미해결 0, 실패 0으로 로테이션이 완료되었다고 표시되면,
QUIRE_MASTER_KEY_RETIRED에서 은퇴 키를 제거하고 다시 배포하세요. 그전까지는 유지하세요. 옮기지 못한 값은 여전히 이전 키 아래로 감싸여 있습니다.
작업이 순회하는 곳
감싸인 DEK를 보관하는 모든 저장소. SEALED_STORES에 있는 것들
(apps/worker/src/key-rotation.ts)이 그렇습니다. 제어 데이터베이스 저장소는
제어 데이터베이스에서 순회하고, 조직 저장소는 조직이 있는 데이터베이스에서
행 수준 보안 아래에서 조직 하나씩 순회합니다. 전용 데이터베이스에 고정된
테넌트는 그 데이터베이스에서 로테이션됩니다. 스키마가 목록에 없는 감싸인 키
컬럼을 얻으면 테스트가 실패하고, 자격 증명 검토가 목록이 놓친 봉인 컬럼을
분류해도 실패합니다.
기록
ops.key_rotation: 로테이션당 한 행. 사유, 요청자, 상태, 합계 (다시 감쌈, 이미 최신, 미해결, 실패)가 담깁니다.ops.key_rotation_progress: 순회가 끝난 저장소·범위당 한 행. 읽지 못한 키 참조와 각 참조 아래에 있던 값의 개수가 담깁니다. 재개된 로테이션은 이것을 건너뜁니다.- 플랫폼 감사 체인: 사유가 담긴
platform/key_rotation_request, 카운트가 담긴 저장소별platform/key_rotation_store하나씩,platform/key_rotation_complete또는platform/key_rotation_fail, 알림용platform/key_age_reminder. - 지표:
quire.secrets.master_key.age와quire.secrets.rewrap.outstanding(지난 로테이션이 옮기지 못한 값).
값이 미해결일 때
미해결 값은 이 설치가 보유하지 않은 키 참조 아래로 감싸여 있거나, 그 컬럼이
약속한 형태가 아닙니다. 진행 기록에는 참조가 명시됩니다(예:
env:QUIRE_MASTER_KEY:v0 (unreadable)). 그 키를 QUIRE_MASTER_KEY_RETIRED에
복원하고 다시 로테이션하거나, 키가 영구히 사라졌다면 조직 관리자가 자격
증명을 다시 입력하게 하세요. 그러면 현재 키 아래에 봉인됩니다. 실패한
로테이션은 기록에 오류를 표시합니다. 원인을 고치고 다시 요청하세요.
서명 키
마스터 키와는 별개입니다. 각 조직은 자체 RSA 키로 OpenID Connect 토큰과
LTI 메시지에 서명하며, 그 키는 /.well-known/jwks.json에 게시됩니다. 여기서
운영자가 할 일은 없습니다. 시간당 platform.signing_keys 스케줄은 현재
키의 구십 일이 다 되기 칠 일 전에 후임을 게시하고, 일주일 뒤 후임이 서명을
시작하며 이전 키는 은퇴 단계로 넘어가고, 그로부터 구십 일 뒤 이전 키는
삭제되어 키셋에서 빠집니다. 모든 단계는 플랫폼 감사 체인의
platform/signing_key_advance 항목입니다.
노출 이후처럼 조직의 키를 일찍 교체하려면:
- 콘솔에서: Security, Master key, Publish a new signing key
(
platform/keys_manage필요); 또는 - 워커 환경의 셸에서:
bun run kek:rotate signing-keys rotate --tenant <slug or id> --reason "Key exposed, INC-3310".bun run kek:rotate signing-keys status는 단계별로 모든 조직의 키를 나열합니다.
새 키는 즉시 게시되고 칠 일 뒤, 현재 키가 은퇴할 때 서명을 시작합니다. 일주일은
의도적입니다. 의존하는 쪽은 키셋을 캐시하므로, 겹침이 짧으면 모든 도구가
한 번에 실패합니다. 은퇴하는 키는 구십 일 동안 더 키셋에 남아 이미 서명한
토큰이 계속 검증됩니다. 노출로 더 일찍 신뢰를 멈춰야 한다면 행을 삭제하는
작업은 변경 기록 아래에서 운영자 자신의 데이터베이스 접근으로 하는
변경입니다(브레이크글라스 접근은 읽기 전용이며), 그 키로 서명된 토큰은
이후 검증에 실패합니다. 강제 로테이션은 사유와 함께 감사 체인의
platform/signing_key_rotate로 남습니다. 워커는 새 키를 감싸기 위해 웹 계층과
같은 QUIRE_MASTER_KEY 설정이 필요하며, 마스터 키용 bun run kek:rotate는
다른 것들과 함께 서명 키도 다시 감쌉니다(oauth_signing_key는
SEALED_STORES에 있습니다).
브레이크글라스 프로덕션 접근
누구도 프로덕션에 대한 상시 접근을 보유하지 않습니다. 기다릴 수 없는 일이 생기면 담당자가 브레이크글라스 승인을 발급합니다. 플랫폼 콘솔, Security, Break-glass access.
- 승인에는 범위(하나의 조직 또는 플랫폼 레지스트리), 사고나 티켓을 명시하는 20자 이상의 사유, 5분에서 240분 사이의 기간이 필요합니다. 만료는 자동입니다. 모든 진술마다 시계와 대조됩니다.
- 발급한 담당자 자신에게, 또는 다른 담당자에게(2인 형태) 발급할 수 있습니다.
발급받은 사람만 사용할 수 있습니다. 발급에는
platform/break_glass_issue가, 사용에는platform/break_glass_use가 필요하며 둘 다 기본적으로 담당자 전용입니다. - 진술은 데이터베이스 로그인이 아니라 게이트웨이를 통해 실행됩니다. 읽기 전용, 한 번에 하나, 조직 또는 제어 레지스트리로 한정, 5초 타임아웃, 최대 500행. 바이너리 값은 크기로 표시됩니다.
- 플랫폼 감사 체인은 발급(사유 포함), 철회, 실행 전의 모든 진술
(
platform/break_glass_statement, 거부된 것은 결과denied), 그리고 모든 결과(platform/break_glass_result)를 기록합니다.ops.break_glass_statement는 감사 항목 ID를 보관하므로 발급 기록이 그 감사 항목과 조인됩니다. - 쓰기는 제공되지 않습니다. 릴리스를 기다릴 수 없는 변경은 이 제품 밖에서 자체 변경 기록 아래의 운영자 데이터베이스 접근으로 하며, 그 기록은 여기서 쓴 사고 참조를 인용해야 합니다.
데이터베이스 자격 증명을 발급하지 않는 이유: Postgres 로그인은 그것을 요청한 세션보다 오래 살아남고, 애플리케이션이 의존하는 행 수준 보안을 우회하며, 이 제품의 감사 체인에 쓸 수 없어 그 진술은 누가 서버 로그를 옮겼는지만큼만 감사됩니다. 게이트웨이는 감사 추적을 접근을 둘러싼 실무가 아니라 접근 자체의 속성으로 만듭니다.
감사 요청에 답하려면: 기간의 승인을 나열하고(Break-glass access), 승인의
이력을 열어 진술과 감사 항목 ID를 확인한 뒤, 플랫폼 감사 체인에서 그 항목을
읽으세요(bun run audit:verify --platform으로 체인이 온전함을 증명합니다).