보안
한 번의 보안 검토에 답하는 한 페이지. 무엇이 남고, 무엇이 다른 곳으로 가고, 누가 그것을 읽을 수 있으며, mcpd에 없는 통제 수단은 무엇인지.
아래 내용은 모두 데몬 자체의 소스와, API를 생성하는 원본인
spec/mcpd.spec.json에서 가져왔습니다. 어떤 통제 수단이 아예
없으면, 그 빈틈을 설문지에서 발견하도록 두지 않고 이 페이지가 없다고
밝힙니다.
mcpd가 저장하는 것
mcpd는 에이전트 대화의 원문을 저장합니다. 그것이 mcpd의 목적이고, 보안 검토도
거기에서 시작해야 합니다. model_generation 종류의 스팬은
input과 output을 담습니다. 모델에 보낸 프롬프트
원문과, 돌아온 텍스트 원문입니다. 대부분의 사용자에게 이것은 자신이 보유한
가장 민감한 데이터입니다.
여섯 가지 시그널에서 자유 형식 텍스트가 저장되는 필드는 다음과 같습니다.
| 시그널 | 담고 있는 내용 |
|---|---|
| 스팬 | input, output, attributes, metadata, 그리고 오류 메시지 |
| 로그 | 메시지, 그리고 구조화된 fields |
| 메트릭 | labels |
| 점수 | reason과 metadata |
| 피드백 | 자유 텍스트 comment와 metadata |
| 관점 노트 | 문장 자체인 text |
페이로드와 함께, 모든 시그널은 하나의 실행을 시스템의 나머지 부분과 이어 주는
상관 정보 필드를 담습니다. 프로젝트, 환경, 서비스 이름과 버전, 세션, 스레드,
사용자, 리소스, 실행, 요청, 실험, 릴리스입니다. user_id는
마스킹을 끄지 않는 한 들어오는 길에 해시됩니다. 나머지는 보낸 그대로
저장되므로, 그중 어느 하나에 신원 정보를 넣었다면 그 필드는 개인을 식별할 수
있는 필드로 다루십시오.
토큰 수와 비용 수치는 수집 시점에 계산되어 실행과 함께 저장됩니다. 모델 호출에 관한 어떤 정보도 제공업체에서 가져오지 않습니다.
어디에 기록되는가
백엔드는 하나이며, 시작할 때 고릅니다. 기본값은 ./mcpd.db에 있는
내장 SQLite 파일이고, --data로 지정합니다. 전문 검색 색인도 같은
파일 안에 있으므로, 저장소는 백업할 파일 하나이자 보호할 파일 하나입니다.
--postgres-url이나 --clickhouse-url을 쓰면 데이터는
직접 운영하는 데이터베이스에 들어갑니다.
지속성 실행을 켜면, 실행은 트레이스 저장소가 무엇이든 첫 번째 파일 옆의 두 번째
SQLite 파일에 삽니다. 이 파일은 모든 실행의 입력, 상태, 출력, 상관 정보와, 모든
단계의 입력, 출력, 오류를 보낸 그대로 보관합니다. 마스킹하지 않고,
user_id도 해시하지 않습니다. 뒤의 단계가 이것을 다시 읽으며, 고쳐
쓴 값은 실행이 하는 일을 바꿀 것이기 때문입니다. 실행이 트레이스 저장소에 쓰는
스팬은 다른 스팬처럼 마스킹됩니다. 실행 파일은 트레이스 저장소만큼, 또는 그보다
더 보호하십시오. 보존 기간과 개인 단위 삭제는 둘 다 이 파일에 닿으며, 끝난 실행이
대상입니다. 실행을
보십시오.
mcpd는 기록하는 내용을 암호화하지 않습니다. 저장 데이터 암호화는 내장 저장소에서는 볼륨의 몫이고, 나머지 두 백엔드에서는 데이터베이스의 몫입니다.
머신 밖으로 나가는 것
기본 설정에서는 아무것도 나가지 않습니다. 데몬이 바깥으로 요청을 보내는 곳은 정확히 네 군데이고, 그 넷은 모두 직접 설정하기 전까지 꺼져 있습니다. 업데이트 확인도, 사용량 보고도, 어떤 종류의 텔레메트리도 없습니다.
| 목적지 | 보내는 내용 | 다음이 아니면 꺼짐 |
|---|---|---|
| 스토리지 백엔드 | mcpd가 저장하는 모든 것이, 직접 연 연결을 통해 나갑니다. | --postgres-url이나 --clickhouse-url을 설정한 경우. 기본 저장소는 로컬 파일이라 네트워크가 전혀 필요 없습니다. |
| 전달 |
저장된 모든 스팬이 OTLP JSON으로 다시 만들어져
{endpoint}/v1/traces로 갑니다. 여기에는
페이로드도 들어갑니다. input과 output은
input.value와 output.value 속성으로 나가며,
각각 32 KiB에서 잘립니다. 이미 마스킹된 값은 마스킹된 채로 나가고,
해시된 user_id는 해시 그대로 전달됩니다.
| --forward-endpoint를 설정했거나, 설정 파일에 forward 블록이 있는 경우. |
| 모니터와 목표 웹훅 | 고정된 JSON 본문입니다. 모니터 또는 목표의 id와 이름, 프로젝트, 조건, 감시 대상, 임계값, 측정값, 표본 수, 한 줄짜리 상세 설명, 타임스탬프가 들어 있습니다. 프롬프트나 응답 텍스트는 들어 있지 않습니다. | 설정 파일의 모니터나 목표가 webhook을 지정한 경우. |
| 판정 모델 |
판정 기준과 함께 그 실행의 질문과 답변이, 각각 4,000자에서 잘려
{endpoint}/chat/completions로 POST됩니다.
저장된 프롬프트와 응답 텍스트를 모델 제공업체로 보내는 경로는 이것
하나뿐입니다.
| 설정 파일의 judge 블록이 엔드포인트와 모델과 기준을 하나 이상 지정했고, 호출자가 그 스코어러를 이름으로 요청한 경우. |
바이너리가 /에서 제공하는 인터페이스는 바이너리 안에 컴파일되어
있습니다. 글꼴과 스타일과 스크립트는 모두 같은 출처에서 오고, 서드파티 요청은
하지 않습니다.
키와 스코프
인증 수단은 API 키입니다. 계정도, 세션도, 로그인도 없습니다. 키는
Authorization: Bearer …로 보내거나 X-Mcpd-Key
헤더에 담아 보내며, 비교하기 전에 양쪽 모두 SHA-256으로 해시합니다. 그래서
설정된 키의 길이도, 처음 틀린 문자의 위치도 응답 시간으로는 알아낼 수
없습니다.
스코프는 여섯 가지이고, 부여하는 범위가 넓어지는 순서로 다음과 같습니다.
| 스코프 | 허용하는 것 | 별도 스코프인 이유 |
|---|---|---|
ingest | 시그널 쓰기. 읽기는 없음. | 이 키는 스팬을 보내는 모든 컨테이너에 들어가고, 여기에는 서드파티 코드를 실행하는 컨테이너도 포함됩니다. 그 키가 저장된 모든 프롬프트까지 열어서는 안 됩니다. |
read | 저장된 시그널 읽기. | 프롬프트가 있는 곳이 바로 읽기입니다. |
write | 저장된 데이터 변경. 점수, 피드백, 데이터셋, 실험, 확인 처리. | 그중 몇몇 핸들러는 처리하는 도중에 페이로드를 읽습니다. 운영 환경의 실행을 데이터셋에 담아 두는 작업은 그 실행의 input과 output을 읽으며, 이는 스팬을 보내는 것보다 큰 권한입니다. |
execute | 실행을 시작하고, 취소하고, 신호를 보내고, 실행에 이벤트를 발행합니다. | 실행을 시작하면 돈이 들고 시스템에 손이 닿습니다. 점수를 기록할 수 있는 키가 작업까지 띄울 수 있어서는 안 됩니다. |
worker | 단계를 가져가고, 그 결과를 보고합니다. | 단계 본문을 실행하는 프로세스 안에 있는 키입니다. 단계를 가져가면 그 단계의 input과 앞선 모든 단계의 output이 함께 돌아오므로 이 키는 페이로드를 읽으며, 실행을 시작하거나 저장된 데이터를 바꿀 수까지 있어서는 안 됩니다. |
admin | 데이터 파기, 그리고 감사 로그 읽기. | 삭제는 읽기가 아닙니다. |
어떤 엔드포인트가 어떤 스코프를 요구하는지는 명세에서 엔드포인트마다 선언되고, 생성된 HTTP API 레퍼런스에 실립니다. 덕분에 이 엔드포인트에 무엇이 필요한가라는 물음의 답이 문서마다 하나씩이 아니라 전체에 하나만 있습니다.
폴백, 그리고 이미 가지고 있는 키에 그것이 뜻하는 것
write 목록을 설정하지 않으면 ingest 목록으로
폴백하고, admin 목록을 설정하지 않으면 read
목록으로 폴백합니다. execute와 worker 목록을
설정하지 않으면 write 목록으로 폴백하고, 그것도 설정하지
않았다면 ingest 목록에 이릅니다. 이 덕분에, 더 좁은 스코프가
생기기 전에 설정해 둔 모든 배포에서 업그레이드가 접근 권한을 끊어 버리는
일이 없습니다. 또한 admin 키는 execute와 worker에도
통하므로, 운영자가 실행을 손으로 움직일 수 있습니다. 이것들을 합치면, ingest
키와 read 키만 가진 데몬에서 다음을 뜻합니다.
- ingest 키가 점수와 피드백도 붙이고, 실행을 시작하고, 단계를 가져갈 수 있고,
- read 키가 트레이스를 삭제할 수도 있으며, admin 폴백을 통해 실행을 시작하고 단계를 가져갈 수도 있습니다.
따라서 --admin-key를 설정하기 전까지 read 키는 곧 삭제 키이며,
--execute-key, --worker-key,
--admin-key를 설정하기 전까지 두 키 모두 실행 표면에 닿습니다.
키는 그 사실을 전제로 나눠 주어야 합니다. 저절로 좁아지는 것은 없습니다.
스코프마다 플래그가 있고, --ingest-key,
--read-key, --write-key,
--admin-key, --execute-key,
--worker-key가 각각 목록을 받습니다. 이들 모두 설정 파일에서
프로젝트별로도 지정할 수 있습니다.
키가 하나도 없으면 모든 스코프가 열려 있습니다
이 기본값은 의도한 것입니다. 노트북에서 처음 실행할 때 자격 증명이 필요해서는
안 되기 때문입니다. 동시에 이것은 노출된 인스턴스에서 가장 위험한 한 가지이기
때문에, 데몬은 루프백이 아닌 주소에 바인딩하면, 닿아서는 안 될 호출자에게
열려 있는 표면마다 시작할 때 경고를 로그에 남깁니다. 각 경고는 그 표면과
그것을 닫는 플래그를 이름으로 짚습니다. 포트를 노출하기 전에
mcpd config를 실행하십시오. 그 호출이 확정한 설정을 출력하고
종료합니다. 스코프마다 한 줄씩 나오고 어떤 스코프가 어떤 목록으로
폴백했는지도 이름을 대므로,
admin auth key required (read keys)는 read 키가 여전히
삭제할 수 있다는 뜻입니다.
Docker와 Kubernetes에 그 출력이
실려 있습니다.
닿을 수 있게 되기 전에 닫아 두기
mcpd --ingest-key "$INGEST" --read-key "$READ" 프로젝트는 테넌시 경계입니다
최상위에 설정한 키는 모든 프로젝트를 허용합니다. 대신 설정 파일의
projects 블록 안에서 이름 붙인 프로젝트 아래에 적은 키는 그
프로젝트만 허용하고 다른 프로젝트는 허용하지 않으며, 프로젝트마다 스코프별
목록을 따로 가집니다. 하나의 키를 여러 프로젝트 아래에 넣을 수 있고, 플랫폼
팀은 이 방식으로 서버 전체를 쥐지 않으면서 자기가 운영하는 범위를 아우르는
키를 가집니다.
쓰기 경로에서 검사는 요청 단위가 아니라 시그널 단위입니다.
- 하나의 배치가 여러 프로젝트를 가리킬 수 있습니다. 키가 닿지 못하는 프로젝트를 가리킨 시그널은 버려지고, 응답에 거부된 것으로 사유와 함께 보고됩니다. 그래서 잘못된 프로젝트를 향하고 있는 발신자 무리는 이미 읽고 있는 응답에서 발견할 수 있습니다. 배치의 나머지는 저장됩니다.
읽기 경로에서는 프로젝트를 지정하는 모든 엔드포인트가 그 프로젝트를 키의 허용
범위와 대조합니다. 트레이스, 스팬, 로그, 메트릭, 점수, 피드백, 분석,
데이터셋, 실험, 감사 로그가 여기에 해당합니다. 데이터셋 안의 항목은 데이터셋의
id로 지정하는데, 데몬은 그 데이터셋을 어느 프로젝트가 소유하는지 조회한 다음
키를 그 프로젝트와 대조합니다. 그래서 프로젝트를 열 수 없는 키는 그
프로젝트에서 담아 둔 케이스도 읽지 못합니다. 존재하지 않는 id는 키가 닿지
못하는 id와 똑같은 방식으로 거부되므로, 이 엔드포인트로 어떤 id가 존재하는지
떠볼 수 없습니다. 프로젝트를 받지 않는 엔드포인트는 셋이며,
어떤 프로젝트로 한정된 키든 read 스코프만 가지고 있으면 닿을 수 있습니다.
프로젝트 목록, 스코어러 카탈로그, 저장소 기능 집합
(GET /v1/api/capabilities)입니다. 프로젝트 목록은 키의 허용
범위로 걸러지므로, 프로젝트로 한정된 키에는 그 키가 닿는 프로젝트만
보입니다. 공유 인스턴스에서 프로젝트 이름은 팀 이름이거나 고객 이름인 경우가
많습니다. 모니터 상태와 목표 상태도 이 목록에 있었지만, 이제는 키의 프로젝트
허용 범위로 걸러집니다. 처음부터 그랬어야 합니다. 모니터는 감시하는
프로젝트를 이름으로 지니고, 보낼 webhook을 함께 들고 있기 때문입니다.
API는 그 webhook의 호스트만 알려 줍니다. webhook URL은 그 자체가 자격 증명인 경우가 많기 때문입니다.
거부는 두 문제 중 어느 쪽인지 밝힙니다
키가 없거나 알 수 없는 키이면 401이고, 아는 키인데 이 엔드포인트나 이 프로젝트가 받아 주지 않으면 403입니다. 이 구분은 운영을 위한 것입니다. 401은 자격 증명에서 오타를 찾아보라는 뜻이고, 403은 자격 증명은 맞고 허용 범위가 틀렸다는 뜻입니다.
$ curl -sw "%{http_code} " "$MCPD/v1/api/traces?project=acme"401 this endpoint needs an API key; send it as `Authorization: Bearer <key>`$ curl … -H "authorization: Bearer $TYPO"401 that key is not one this daemon knows$ curl … -H "authorization: Bearer $INGEST"403 that key is not valid for reading$ curl … -H "authorization: Bearer $BILLING_READ"403 that key does not reach the project "acme" 마지막 메시지는 그 프로젝트가 존재하든 존재하지 않든 똑같습니다. 어떤 프로젝트 이름이 진짜인지 확인해 주는 것은 작은 유출이고, 공유 데몬에서 그 이름은 곧 고객의 이름입니다.
감사 로그
저장된 페이로드를 mcpd 밖으로 내주는 일은 기록되고, 모든 삭제도 기록됩니다.
그래서 이 실행을 누가 읽었는가라는 물음에 답이 있습니다. 한 행은 언제, 어떤
키로, 무슨 동작을, 어느 프로젝트에서, 어떤 트레이스나 데이터셋을 지정해
했는지, 그리고 그 요청이 처리되었는지 거부되었는지를 담습니다.
GET /v1/api/audit가 최신 순으로 읽어 오며,
admin 스코프를 요구합니다.
기록되는 동작은 열여덟 가지입니다. get_trace,
list_spans, list_logs, list_feedback,
list_dataset_items, capture_into_dataset,
read_trace_aspect_notes, get_theme, get_theme_flow, list_theme_history, list_theme_examples,
delete_trace, erase_subject, 그리고 지속성 실행에
관한 다섯 가지인 list_runs, get_run,
list_run_events, get_step_cause,
cancel_run입니다. 기준선은 요청이 아니라 페이로드입니다.
트레이스 목록은 이름, 시간, 토큰 수, 비용으로 이루어진 요약을 돌려주고 그
안에는 프롬프트도 응답도 없으므로 기록되지 않습니다. 그중 한 트레이스를 여는
일은 기록됩니다. 텍스트가 있는 곳이 거기이기 때문입니다. 지속성 실행 목록은
기록됩니다. 목록의 모든 실행이 자기 입력, 출력, 상태를 보낸 그대로 싣고 있기
때문입니다. 실행에 대한 거부된 읽기는 그 실행을 소유한 프로젝트 아래에
기록됩니다.
워커 호출은 기록되지 않습니다. 단계 클레임, 시작 선언, 하트비트, complete,
fail입니다. 리스에는 실제로 단계의 입력과 그 앞 단계들의 출력이 실려 있지만,
워커 루프는 1분에 여러 번 클레임하고, 클레임마다 행을 하나씩 남기면 사람이 한
읽기가 그 아래에 묻힙니다. worker 키는 자기가 처리하는 실행의 모든
페이로드를 읽는 키로 다루십시오. 실행을 시작하는 일도 기록되지 않습니다. 보관
중인 멱등성 키로 다시 보내면 그 키가 시작한 실행이 답으로 돌아오므로,
execute 키는 자기가 시작하는 실행을 읽을 수 있는 키로 다루십시오.
검토자가 알아야 할 다섯 가지 성질입니다.
- 키 자체는 결코 기록되지 않습니다. 주체는 그 키의 SHA-256
다이제스트를 잘라 낸 값이고, 이 값은 키를 서로 구분해 주면서도 그 키를
검증할 수 있는 값을 남기지 않습니다. 열려 있는 데몬에 키 없이 들어온 요청은
anonymous로 기록됩니다. - 사람의 신원은 인그레스에서 오거나, 아예 오지 않습니다.
mcpd에는 사용자 계정이 없으므로, 그 자체로는 한 행이 동작을 사람이 아니라
키에 귀속시킵니다.
--trusted-identity-header와--trusted-proxy-cidr이 둘 다 설정된 경우에만subject필드가 신원을 담고, 그 밖에는 비어 있으며 비어 있는 쪽이 기본값입니다. mcpd는 그 신원을 스스로 확정하지 않습니다. 지정한 주소가 주장한 내용을 기록할 뿐입니다. - 거부도 기록됩니다. 403을 돌려주기 전에 행이 먼저 남습니다. 다른 팀의 프로젝트에 대한 거부된 시도야말로 감사 로그가 존재하는 이유이기 때문입니다. 로그를 읽는 일 자체는 기록되지 않습니다. 페이로드를 한 번도 읽지 않았는데도 로그가 계속 커질 것이고, 그 행은 admin 키를 가진 사람이 이미 아는 것 외에 아무것도 말해 주지 않을 것이기 때문입니다.
- 어떤 키인지는 기록하고, 누구인지는 선택입니다. 키는 잘라 낸
다이제스트로 저장되고 키 자체는 결코 저장되지 않으므로, 이 로그는 자격
증명이 쌓이는 자리가 아닙니다. 이 방식은 어떤 admin 키가 이것을 삭제했다는
답은 주지만 어떤 사람이 했는지는 주지 않습니다. mcpd에는 ID 공급자가
없기 때문입니다. 두 번째 답까지 원한다면, 인그레스가
이미 붙이고 있는 헤더와 그 헤더를 붙일 수 있는 주소를 알려 주십시오.
--trusted-identity-header x-forwarded-user와--trusted-proxy-cidr 10.42.0.0/16입니다. 두 쪽 모두 필요합니다. 헤더는 그 포트에 닿을 수 있는 사람이면 누구나 보낼 수 있는 문자열이므로, mcpd는 지정한 주소에서 온 것만 읽습니다. 다른 곳에서 온 요청은 평소대로 처리되고 그 행에는 주체가 남지 않으며, 이것이 추측 대신 내놓는 정직한 답입니다. 두 플래그를 모두 설정하지 않으면 주체는 결코 기록되지 않고, 그것이 기본값입니다. - 보존 기간이 이것을 잘라 내지 않습니다. 보존 기간 정리는 스팬, 로그, 메트릭, 트레이스, 점수, 피드백, 관점 노트, 끝난 지속성 실행을 지웁니다. 감사 로그는 남겨 둡니다. 자기가 설명하는 데이터와 함께 만료되는 로그는 삭제된 데이터를 누가 읽었는지 말해 줄 수 없기 때문입니다.
- 자기가 설명하는 요청을 결코 실패시키지 않습니다. 기록하지
못한 행은 로그에 남고
/metrics에서mcpd_audit_write_failures_total로 집계되며, 읽기는 그대로 응답합니다. 읽기 경로를 멈춰 세울 수 있는 감사 로그는 그것이 막으려는 위험보다 더 큰 부담이므로, 요청이 실패하기를 기대하지 말고 그 카운터를 감시하십시오.
어떤 키도 감싸지 않는 것
- 인터페이스.
/의 마크업은 그 포트에 닿을 수 있는 누구에게나 제공됩니다. 그 뒤의 데이터는 그렇지 않습니다. 인터페이스가 보내는 모든 요청은 조회 API를 거치며 같은 키를 만납니다. 데몬 안에 로그인 화면은 없습니다. - 프로브와
/metrics. 일부러 인증을 받지 않습니다. kubelet과 스크레이퍼는 키를 들고 다닐 수 없기 때문입니다. 상태, 버전, 가동 시간, 스토리지 백엔드의 이름과 카운터를 보고하며, 페이로드는 결코 보고하지 않습니다. - 오리진. 조회 API와 프로브는
Access-Control-Allow-Origin: *로 응답하므로, 어떤 오리진의 브라우저 페이지든 이들을 호출할 수 있습니다. mcpd는 쿠키도, 자동으로 실리는 자격 증명도 쓰지 않으므로 그런 페이지도 키를 가지고 있어야 합니다. 오리진을 열어 둔 정책이 키까지 쥐여 주지는 않습니다.
마스킹이 하는 일과, 할 수 없는 일
마스킹은 기본으로 켜져 있고, 수집 파이프라인의 세 번째 단계로서 쓰기 전에
실행됩니다. 규칙에 걸린 값은 데이터베이스에 아예 닿지 않습니다. 이것은 나중에
삭제된 값과는 다른 보장이고, 감사자가 묻는 것이 바로 그 차이입니다. 마스킹이
할 수 없는 일은 바이트가 네트워크를 타지 않게 막는 것입니다. 규칙이 실행될 때
그 바이트는 이미 네트워크를 건너온 뒤입니다. 그쪽은 SDK가 맡습니다.
MCPD_CAPTURE_PAYLOADS=false는 스팬이 큐에 들어가기 전에
input과 output을 버리고 나머지는 그대로 둡니다.
-
Decode
OTLP or native, gzip or plain
-
Sample
Decided once, at the root
-
Redact
Before anything is written
-
Price
From the configured price book
-
Store
One transaction per batch
Redaction is stage three, not a cleanup job. A value that a rule matches never reaches storage at all.
규칙은 소문자 부분 문자열의 집합과 모드 하나로 이루어집니다. 키 이름을 소문자로 바꾼 값이 그중 하나를 포함하면 규칙에 걸립니다. 걸린 값은 마스킹되거나, 일부만 마스킹되거나, 키를 사용한 다이제스트로 해시되거나, 버려집니다. 걸린 키가 객체를 담고 있으면 그 아래의 모든 것도, 자식 키 이름이 무엇이든 함께 마스킹됩니다. JSON 숫자로 보낸 비밀 값도 문자열로 보낸 것과 똑같이 다룹니다. 전체 규칙 표는 마스킹과 보존 기간에 있습니다.
이제 한계입니다. 보안 검토가 반드시 묻는 부분이고, 설정을 아무리 바꿔도
없어지지 않습니다. 규칙은 값의 모양이 아니라 필드 이름에
걸립니다. 사용자가 문장 한가운데에 붙여 넣은 API 키가
content나 question이라는 필드에 담겨 들어오면,
파이프라인의 어떤 규칙에도 걸리지 않습니다. 필드 이름에는 문제가 없으므로 값은
그대로 저장됩니다.
그래서 마스킹이 막는 것은 한 가지로 분명합니다.
- 자격 증명이 자기 정체를 그대로 이름으로 쓴 필드에 담겨 들어오는, 구조화된 형태의 자격 증명과 식별자 수집을 막습니다.
- 자유 텍스트를 훑지 않으며, 내용 분류기가 아닙니다.
- 비밀 값을 모양이나 엔트로피로 찾아내지 않습니다.
- 스팬의 이름이나 로그 레코드의 메시지는 건드리지 않습니다. 이들은 페이로드가 아니라 묶는 데 쓰는 식별자이고, 고쳐 쓰면 묶음이 깨집니다. 고객의 이메일 주소로 스팬 이름을 만들지 마십시오.
- 실행 저장소는 건드리지 않습니다. 지속성 실행의 페이로드는 그 실행의 작업 상태이므로 보낸 그대로 보관됩니다. 실행이 트레이스 저장소에 쓰는 스팬만 마스킹됩니다.
모양을 보고 동작하는 통제 수단이 하나 있는데, 이것은 예방이 아니라 사후
탐지입니다. 내장 no_leaked_secret 스코어러는 답변에
자격 증명처럼 보이는 토큰이 들어 있는 실행의 점수를 깎습니다. 알려진 접두사로
판단하거나, 대소문자가 섞인 긴 영숫자 연속으로 판단합니다. 이미 저장된 실행에
점수를 매기는 것입니다. 아무것도 지우지 않고, 프롬프트를 읽지 않으며, 요청할
때만 실행됩니다. 이런 일이 일어났다는 사실을 알아내는 수단으로 다루고,
마스킹의 두 번째 방어선으로 여기지 마십시오.
규칙에 걸렸든 걸리지 않았든 두 가지 한도가 적용됩니다. 128 KiB보다 긴 문자열은 문자 경계에서 표시와 함께 잘리고, 순회는 깊이 32에서 멈춥니다. 둘 다 폭주한 문서 하나가 데이터베이스의 크기를 정하거나 CPU를 붙잡아 두지 못하게 하려고 있습니다.
--no-redact는 파이프라인 전체를 끕니다. 상류에서 이미 마스킹하고
있을 때에만 합리적인 선택입니다. 마스킹이 식별자를 해시하는 곳에서는
--redact-key를 설정하십시오. 내장 키는 공개된 소스에 있는
상수이고 식별자는 좁은 공간에서 나오므로, 키 없는 다이제스트는 후보를
추측하는 것만으로 복원할 수 있습니다.
보존 기간과 삭제
--retention-days는 지정한 일수보다 오래된 시그널을 지웁니다. 이
플래그를 빼면 아무것도 지워지지 않으며, 그것이 옳은 기본값입니다. 지난달
트레이스를 조용히 버리는 도구는 하필 가장 나쁜 순간에 그 사실이 드러납니다.
이 정리는 데몬 안에서 한 시간마다 도는 타이머로 실행되며, 사용자의 요청
안에서는 결코 실행되지 않습니다. projects 블록에 이름이 적힌 모든
프로젝트를 대상으로 하고, 프로젝트가 retention_days를 직접
정했으면 그 값을, 정하지 않았으면 전역 값을 씁니다. 기본 프로젝트도 함께
대상입니다. 어디에도 기간이 정해지지 않은 프로젝트는 전혀 정리되지 않습니다.
한 사람에 대한 요청에는
DELETE /v1/api/subjects/{user_id}?project=…를 씁니다. 그
프로젝트에서 그 사람 앞으로 기록된 모든 실행과, 거기에 딸린 스팬, 로그,
메트릭, 점수, 피드백을 지우고, 시그널별 건수로 응답합니다. 식별자는 대조하기
전에 같은 키로 해시되므로, 직접 해시하지 않아도 해시된 식별자를 상대로 요청이
동작합니다. 그리고 응답은 저장된 식별자 가운데 요청이 대조할 수 없는 형태였던
것이 몇 건인지 알려 줍니다. 한 사람이 아니라 한 실행만 지우려면
DELETE /v1/api/traces/{trace_id}가 그 실행과 거기에 딸린
모든 것을 한 트랜잭션으로 지웁니다. 둘 다 admin 스코프를
요구하고, 둘 다 감사 로그에 기록됩니다.
마스킹과 보존 기간이 삭제
요청과, 그것이 약속할 수 있는 것과 없는 것을 차례로 설명합니다.
mcpd가 하지 않는 것
다음 중 어느 것도 이 빌드에는 없습니다. 여기에 적어 두는 이유는, 나중에 검토에서 발견되는 쪽이 여기서 읽고 넘어가는 쪽보다 나쁜 결과이기 때문입니다.
| mcpd에 없는 것 | 대신 할 일 |
|---|---|
| TLS 종료. mcpd는 평문 HTTP로 통신합니다. 프롬프트와 응답이 그 구간을 그대로 지나갑니다. | 인그레스나 메시에서 TLS를 종료하고, 데몬의 포트를 신뢰할 수 없는 네트워크에 두지 마십시오. |
| SSO. ID 공급자 연동도, OIDC도, SAML도, 로그인 화면도 없습니다. | 이미 운영하고 있는 인증 프록시 뒤에 인터페이스를 두십시오. 사람의 신원은 그 프록시가 확정하는 것이 전부입니다. |
| SCIM. 사용자나 그룹 프로비저닝이 없습니다. mcpd에는 사용자 계정이라는 개념이 없기 때문입니다. | 프로비저닝은 프록시에서 하십시오. mcpd 안에서 단위는 키이고, 목록에 넣거나 빼는 것이 전부입니다. |
| 여섯 스코프를 넘어서는 역할. 인가는 스코프 하나와 프로젝트 목록입니다. 엔드포인트별 역할도, 그룹도 없습니다. | 키와 프로젝트로 나누십시오. 어떤 구분이 중요하다면 그것을 프로젝트로 만드십시오. |
| 사람이 누구인지 확정하는 일. 감사 로그가 주체를 기록할 수는 있지만, mcpd가 호출자가 누구인지 알아내지는 않습니다. 인그레스가 주장한 내용을, 그것도 지정한 주소에서 온 것만 기록합니다. | --trusted-identity-header와 --trusted-proxy-cidr을 설정해서, 프록시가 이미 붙이고 있는 헤더가 감사 행까지 닿게 하십시오. 이 둘이 없으면 한 행은 동작을 키에 귀속시키며, 팀마다 키를 하나씩 발급하면 그 다이제스트가 가리키는 범위가 좁아집니다. |
키 수명 주기. 키는 시작할 때 읽는 목록입니다. 만료도, 교체 일정도, 키별 할당량도 없습니다. 존재하는 상한은 프로젝트 단위입니다. --quota-per-minute가 한 프로젝트가 1분 동안 저장할 수 있는 시그널 수를, 어떤 키가 보냈든 상관없이 제한합니다. | 팀이나 프로세스 무리마다 하나씩, 키를 여러 개 설정하십시오. 그러면 키 하나를 폐기하는 일이 스팬을 보내는 모든 것을 다시 배포하는 일이 아니라, 설정을 고치고 재시작하는 일이 됩니다. 어떤 무리에 자기만의 상한이 필요하다면 그 무리를 별도 프로젝트로 만드십시오. |
| 저장 데이터 암호화. mcpd는 평문 파일이나 평문 행을 씁니다. | 볼륨을 암호화하거나, 암호화해 주는 데이터베이스를 쓰십시오. |
제보하기
취약점으로 의심되는 내용은 공개 이슈가 아니라, 제목에 Security를 넣어
[email protected]로 보내 주십시오. 저장소의
SECURITY.md가 범위와 좋은 제보에 무엇이 들어가야 하는지를 밝혀
둡니다. 누군가 스스로 노출하기로 한 인증 없는 인스턴스는 발견 사항이 아니라
문서에 적힌 기본값입니다. 실제로 설정해 둔 키를 통과하거나, 설정된
마스킹 규칙을 통과하거나, 프로젝트 경계를 넘어가는 것이 있다면 그것이 발견
사항입니다.
다음
- 규칙 표, 네 가지 모드, 키를 사용한 해시: 마스킹과 보존 기간.
- 포트가 닿을 수 있게 되기 전에 설정할 것: Docker와 Kubernetes.
- 모든 플래그와 환경 변수: 설정.
- 코드에서, 또는 에이전트에서 배포된 인스턴스에 접속하기: 연동.