감사 로그
변조 감지 Merkle 감사 체인, HMAC 서명, 조회/검증 API, 그리고 웹훅 감사 훅에 대한 설명입니다.
Revka는 명령 실행, 파일 접근, 설정 변경, 인증 결과, 정책 위반 등 모든 보안 관련 행위를 변조 감지 Merkle 해시 체인 감사 로그에 기록합니다. 각 항목은 이전 항목과 SHA-256으로 연결되므로, 어떤 레코드를 변경·삽입·삭제하더라도 체인이 끊어져 탐지가 가능합니다. 선택적으로 각 항목에 HMAC-SHA256 서명을 적용하고, REST API로 감사 내역을 조회하며, 필요 시 체인 무결성을 검증할 수 있습니다.
이 페이지에서는 감사 로깅 활성화, 서명 설정, 이벤트 형식 파악, 그리고 웹훅 감사 훅을 통한 SIEM 연동 방법을 설명합니다.
감사 로그 활성화
섹션 제목: “감사 로그 활성화”감사 로깅은 ~/.revka/config.toml의 [security.audit] 섹션에서 설정하며, 기본적으로 활성화되어 있습니다.
[security.audit]enabled = true # default truelog_path = "audit.log" # relative to the revka/workspace dirmax_size_mb = 100 # rotate when the file reaches this sizesign_events = false # optional HMAC-SHA256 signing (see below)| 키 | 타입 | 기본값 | 설명 |
|---|---|---|---|
enabled | bool | true | 마스터 스위치. false로 설정하면 디스크에 파일이 생성되지 않고 API는 빈 결과를 반환합니다. |
log_path | string | "audit.log" | 로그 파일 경로로, 워크스페이스 디렉터리를 기준으로 해석됩니다. |
max_size_mb | u32 | 100 | 활성 로그가 이 크기(MB)에 도달하면 번호가 붙은 아카이브로 로테이션됩니다. |
sign_events | bool | false | true이면 각 항목에 HMAC-SHA256 서명이 적용됩니다. REVKA_AUDIT_SIGNING_KEY가 필요합니다. |
로거는 새로 설치 시 로그 파일을 미리 생성하여 독자가 “파일 없음” 오류를 겪지 않도록 하며, 재시작 시 마지막 항목에서 체인 상태를 복원하여 새로운 쓰기가 기존 체인을 이어가도록 합니다.
Merkle 체인 동작 방식
섹션 제목: “Merkle 체인 동작 방식”모든 항목은 페이로드 외에 세 가지 체인 연결 필드를 포함합니다.
sequence—0부터 시작하는 단조 증가 카운터.prev_hash— 이전 항목의entry_hash. 첫 번째(제네시스) 항목은 64개의 0으로 구성된 고정 시드를 사용합니다.entry_hash—SHA-256(prev_hash || canonical_JSON_of_content). 여기서 content는prev_hash와entry_hash를 제외한 항목의 페이로드 필드입니다(sequence는 포함).
각 해시가 이전 해시를 포함하므로, 어떤 항목을 수정하면 그 entry_hash가 달라지고, 이는 다음 항목의 prev_hash와 일치하지 않게 되어 그 이후의 모든 레코드가 무효화됩니다. 시퀀스 번호는 반드시 연속적이어야 하므로 항목 삭제 시 간격으로 탐지할 수 있습니다.
쓰기는 뮤텍스와 배타적 파일 잠금으로 직렬화되어(두 데몬이 잠시 겹쳐 실행되더라도 바이트가 뒤섞이지 않음) 각 추가 쓰기는 내구성을 위해 fsync로 플러시됩니다. 시퀀스와 prev_hash는 쓰기가 성공한 후에만 증가하므로 I/O 실패로 인한 간격이 발생할 수 없습니다.
이벤트 형식
섹션 제목: “이벤트 형식”항목은 줄당 하나의 JSON 객체(JSONL)로 기록됩니다. 서명이 적용된 명령 실행 항목의 예시는 다음과 같습니다.
{ "timestamp": "2026-06-18T10:00:00Z", "event_id": "f1c2…-uuid", "event_type": "command_execution", "actor": { "channel": "telegram", "user_id": "123", "username": "@alice" }, "action": { "command": "ls -la", "risk_level": "low", "approved": false, "allowed": true }, "result": { "success": true, "exit_code": 0, "duration_ms": 15, "error": null }, "security": { "policy_violation": false, "rate_limit_remaining": 19, "sandbox_backend": "firejail" }, "sequence": 42, "prev_hash": "<64 hex chars>", "entry_hash": "<64 hex chars>", "signature": "<64 hex chars>"}signature 필드는 sign_events = true일 때만 존재합니다. actor, action, result 객체는 이벤트 유형에 따라 없을 수 있습니다.
이벤트 유형
섹션 제목: “이벤트 유형”event_type의 값은 다음 중 하나입니다.
| 값 | 기록 시점 |
|---|---|
command_execution | 셸 명령이 실행될 때 (위험 수준, 승인 여부, 허용/차단, 소요 시간 포함). |
file_access | 파일 작업이 평가될 때. |
config_change | 설정이 변경될 때. |
auth_success | 인증에 성공할 때 (예: 클라이언트 페어링 완료). |
auth_failure | 인증에 실패할 때 (예: 잘못된 페어링 코드). |
policy_violation | 보안 정책이 행위를 차단할 때. |
security_event | 그 밖의 보안 관련 이벤트 (WebSocket 연결, 페어링 흐름). |
HMAC 서명
섹션 제목: “HMAC 서명”해시 체인만으로는 변조를 감지할 수 있지만, 파일 전체를 재작성할 수 있는 공격자는 모든 해시를 재계산할 수 있습니다. sign_events를 활성화하면 각 entry_hash에 비밀 키 기반의 HMAC-SHA256 서명이 추가되어, 키 없이는 위조된 체인에 서명할 수 없습니다.
-
32바이트 키(64개의 16진수 문자)를 생성하고, 게이트웨이 시작 전에
REVKA_AUDIT_SIGNING_KEY환경 변수로 내보내십시오 — 키는 로거 초기화 시 한 번만 로드됩니다.Terminal window export REVKA_AUDIT_SIGNING_KEY="$(openssl rand -hex 32)"Terminal window $bytes = New-Object byte[] 32[System.Security.Cryptography.RandomNumberGenerator]::Fill($bytes)$env:REVKA_AUDIT_SIGNING_KEY = ($bytes | ForEach-Object { $_.ToString('x2') }) -join '' -
설정에서 서명을 활성화합니다.
[security.audit]enabled = truesign_events = true -
데몬을 시작합니다. 이후 기록되는 모든 항목에
signature가 포함됩니다.
서명된 레코드와 서명되지 않은 레코드는 순방향 호환됩니다. 서명 활성화 이전에 기록된 항목이 섞여 있는 체인도 문제없이 검증됩니다. signature가 있는 레코드는 키가 있을 때 검증되고, 없는 레코드는 건너뜁니다.
감사 로그 API
섹션 제목: “감사 로그 API”두 엔드포인트 모두 게이트웨이 API의 일부이며, 페어링 흐름에서 발급한 bearer 토큰이 필요합니다.
최근 이벤트 조회
섹션 제목: “최근 이벤트 조회”GET /api/audit?limit=50&event_type=command_execution&since=2026-01-01T00:00:00ZAuthorization: Bearer <token>| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
limit | int | 50 | 반환할 최대 이벤트 수. 500이 상한입니다. |
event_type | string | — | 특정 이벤트 유형으로 필터링합니다 (위의 표 참조). |
since | string | — | RFC 3339 타임스탬프 하한. |
이벤트는 최신순으로 반환됩니다.
{ "events": [ /* AuditEvent objects, newest first */ ], "count": 50, "audit_enabled": true}감사 로깅이 비활성화된 경우, 엔드포인트는 오류 대신 {"events": [], "count": 0, "audit_enabled": false}를 반환하여 대시보드가 정상적으로 저하 동작을 수행할 수 있습니다.
curl -s "http://127.0.0.1:42617/api/audit?limit=20&event_type=policy_violation" \ -H "Authorization: Bearer rk_<token>"체인 무결성 검증
섹션 제목: “체인 무결성 검증”GET /api/audit/verifyAuthorization: Bearer <token>활성 로그를 다시 읽어 모든 entry_hash가 올바르게 재계산되는지, 모든 prev_hash가 이전 항목과 연결되는지, 시퀀스 번호가 0부터 연속적인지, 그리고 서명 키가 있는 경우 모든 서명이 일치하는지 확인합니다.
{ "verified": true, "entry_count": 128 }첫 번째 위반 발생 시 이유와 함께 반환합니다.
{ "verified": false, "error": "entry_hash mismatch at line 43 (sequence 42): expected …, got …" }curl -s http://127.0.0.1:42617/api/audit/verify \ -H "Authorization: Bearer rk_<token>"감사 로깅이 비활성화된 경우 응답은 {"verified": false, "error": "Audit logging not enabled"}입니다.
로그 로테이션
섹션 제목: “로그 로테이션”활성 로그가 max_size_mb에 도달하면 로테이션이 발생합니다. audit.log는 audit.log.1.log가 되고, 기존 아카이브는 번호가 하나씩 올라가며(.1 → .2, … .9 → .10), 새로운 audit.log가 시작됩니다.
로테이션은 새 파일에 대해 체인을 제네시스부터 재시작하므로 /api/audit/verify는 활성 로그에 대해 즉시 성공합니다. 로테이션된 각 audit.log.N.log 아카이브는 자체적으로 완결된 체인이며 독립적으로 검증 가능합니다 — 연속적인 이력이 필요하다면 아카이브를 보관하십시오.
웹훅 감사 훅
섹션 제목: “웹훅 감사 훅”실시간 푸시 기반 감사 전달 — SIEM, 컴플라이언스 파이프라인, 중앙 집중식 로그 저장소에 연결 — 을 위해 내장 WebhookAuditHook을 사용하십시오. 이 훅은 설정된 글로브 패턴과 이름이 일치하는 모든 툴 호출에 대해 외부 HTTPS 엔드포인트로 JSON 페이로드를 POST하며, /api/audit을 폴링할 필요가 없습니다.
[hooks.builtin.webhook_audit]에서 설정합니다.
[hooks]enabled = true
[hooks.builtin.webhook_audit]enabled = trueurl = "https://siem.example.com/revka/audit"tool_patterns = ["Bash", "Write", "mcp__*"]include_args = falsemax_args_bytes = 4096| 키 | 타입 | 기본값 | 설명 |
|---|---|---|---|
enabled | bool | false | 훅을 활성화합니다. |
url | string | "" | 대상 엔드포인트. https://여야 합니다 (보안 참고 사항 참조). 값이 비어 있으면 활성화 시 경고를 기록하고 이벤트를 버립니다. |
tool_patterns | list | [] | 감사할 툴 이름의 글로브 패턴 (* 와일드카드, 예: mcp__*, *_write). 빈 목록이면 아무것도 감사하지 않습니다. |
include_args | bool | false | 페이로드에 툴 인수를 포함합니다. 시크릿이나 개인정보가 포함될 수 있으므로 필요한 경우에만 활성화하십시오. |
max_args_bytes | u64 | 4096 | 직렬화된 인수를 이 바이트 수로 자릅니다 (0 = 무제한). |
POST 본문의 형식은 다음과 같습니다.
{ "event": "tool_call", "timestamp": "2026-06-18T10:00:00Z", "tool": "Bash", "success": true, "duration_ms": 42, "error": null, "args": null}args는 include_args = false인 경우(또는 캡처된 인수가 없는 경우) null입니다. 전달은 fire-and-forget 방식으로, 실패 시 로그에 기록되지만 에이전트를 차단하거나 중단시키지 않습니다. 훅은 수명 주기 파이프라인의 마지막 단계에서 실행됩니다.
경량 로컬 로깅
섹션 제목: “경량 로컬 로깅”외부 엔드포인트 없이 툴 호출을 데몬의 구조화 로그에 출력하기만 원한다면, 대신 CommandLoggerHook을 활성화하십시오 — 완료된 각 툴 호출의 툴 이름, 소요 시간, 성공 여부를 tracing에 기록합니다.
[hooks.builtin]command_logger = true관련 페이지
섹션 제목: “관련 페이지”- 정책, 명령 및 샌드박싱 —
command_execution및policy_violation이벤트를 생성하는 요소. - 시크릿, 페어링 및 디바이스 인증 —
auth_success/auth_failure뒤의 bearer 토큰. - OTP 게이팅 및 긴급 정지 — 감사 결과가 기록되는 추가 게이트.
- 비용, 감사, ClawHub 및 자격증명 API — 전체 API 레퍼런스의 감사 엔드포인트.
- 관찰 가능성 및 트레이싱 — 훅 파이프라인 및 런타임 트레이스 진단.
- 로그, 감사, doctor, 페어링 및 스킨 페이지 — 대시보드 감사 뷰어.