ZeroClaw — Security Policy
Layer 3 학습. 실제 소스 코드 기반. 파일:
src/security/policy.rs,src/security/mod.rs
전체 구조
SecurityPolicy
│
├─ AutonomyLevel ← ReadOnly / Supervised / Full
├─ 커맨드 허용/차단 정책 ← allowed_commands, 위험도 분류
├─ 경로 접근 정책 ← workspace_only, forbidden_paths, allowed_roots
├─ Rate limiting ← ActionTracker (슬라이딩 윈도우, 1시간)
└─ Tool operation gate ← Read 항상 허용, Act는 autonomy + rate 체크
보조 모듈:
audit.rs — 보안 이벤트 로깅
secrets.rs — AES 암호화 자격증명 저장
prompt_guard.rs — 프롬프트 인젝션 방어
leak_detector.rs — 응답 내 민감 정보 유출 감지
pairing.rs — 채널 장치 페어링 인증
estop.rs — 긴급 정지 (E-Stop)
sandbox (Docker/Firejail/Bubblewrap/Landlock) — OS 레벨 격리
AutonomyLevel
pub enum AutonomyLevel {
ReadOnly, // 관찰만, 실행 불가
Supervised, // 실행 가능하지만 위험 작업은 승인 필요 (기본값)
Full, // 정책 범위 내 완전 자율 실행
}
실제 동작 차이:
| | ReadOnly | Supervised | Full | |---|---|---|---| | shell 명령 실행 | ❌ | ✅ (위험도 체크) | ✅ (위험도 체크) | | High risk 명령 | ❌ | 명시적 승인 필요 | 명시적 허용 시 가능 | | Medium risk 명령 | ❌ | 승인 필요 | 승인 없이 실행 | | Read tool | ✅ | ✅ | ✅ | | Act tool | ❌ | ✅ + rate limit | ✅ + rate limit | | system prompt | "read-only" 안내 | "ask before acting" | "execute directly" |
SecurityPolicy 필드
pub struct SecurityPolicy {
pub autonomy: AutonomyLevel, // 기본: Supervised
pub workspace_dir: PathBuf, // 작업 디렉토리
pub workspace_only: bool, // 기본: true (workspace 밖 접근 차단)
pub allowed_commands: Vec<String>, // 허용 명령어 목록
pub forbidden_paths: Vec<String>, // 금지 경로 목록
pub allowed_roots: Vec<PathBuf>, // workspace 외 허용 경로
pub max_actions_per_hour: u32, // 기본: 20
pub max_cost_per_day_cents: u32, // 기본: 500 (5달러)
pub require_approval_for_medium_risk: bool, // 기본: true
pub block_high_risk_commands: bool, // 기본: true
pub shell_env_passthrough: Vec<String>, // 허용 환경변수
pub tracker: ActionTracker, // 슬라이딩 윈도우 카운터
}
기본 allowed_commands (Unix):
git, npm, cargo, ls, cat, grep, find, echo, pwd,
wc, head, tail, date, df, du, uname, uptime, hostname
(Linux 추가: free)
기본 forbidden_paths:
/etc, /root, /home, /usr, /bin, /sbin, /lib, /opt,
/boot, /dev, /proc, /sys, /var, /tmp,
~/.ssh, ~/.gnupg, ~/.aws, ~/.config
커맨드 검증 파이프라인
validate_command_execution(command, approved)
│
├─ 1. is_command_allowed() ─ 5중 차단 게이트
│ ├─ ReadOnly → 즉시 false
│ ├─ subshell 차단: `, $(), <(), >()
│ ├─ redirect 차단: 언쿼트 > < (쓰기/읽기 경로 우회 방지)
│ ├─ tee 차단 (pipe를 통한 파일 쓰기 우회 방지)
│ ├─ 단독 & 차단 (백그라운드 체이닝, && 는 허용)
│ └─ 세그먼트별 allowed_commands 검사
│ (;, &&, ||, |, 개행으로 분리 — 쿼트 인식)
│
├─ 2. command_risk_level()
│ ├─ High: rm, sudo, curl, wget, ssh, shutdown, ...
│ ├─ Medium: git commit/push/reset, npm install, touch, mv, ...
│ └─ Low: 그 외
│
├─ 3. block_high_risk_commands 체크
│ → true이면 High 차단
│ → 단, allowed_commands에 명시적으로 있으면 우회 허용
│ ("*" 와일드카드는 명시적 허용 아님 → 우회 불가)
│
└─ 4. autonomy × approval 게이트
High + Supervised + !approved → 에러
Medium + Supervised + require_approval + !approved → 에러
핵심 설계 원칙:
와일드카드(
*)로 모든 명령을 허용해도block_high_risk_commands는 여전히 작동.rm을 명시적으로 허용한 경우만 High risk 게이트 우회 가능.
커맨드 파싱 — Quote-aware 쉘 렉서
"sqlite3 db \"SELECT 1; SELECT 2;\""
→ 세그먼트: ["sqlite3 db \"SELECT 1; SELECT 2;\""] ← 쿼트 내 세미콜론 무시
"ls; rm -rf /"
→ 세그먼트: ["ls", "rm -rf /"] ← 두 번째 세그먼트 차단
"echo \"A>B\""
→ redirect 없음 (쿼트 내 > 무시) ← 허용
"cat $HOME/.ssh/id_rsa"
→ 언쿼트 $HOME → 차단 ← 쉘 변수 확장 차단
차단하는 인젝션 패턴:
;\n세미콜론/개행 체이닝|파이프 (세그먼트로 분리해서 각각 검증)&&||논리 연산자 체이닝&백그라운드 실행`$()서브쉘$VAR${VAR}쉘 변수 확장<()>()프로세스 치환><>>리다이렉션tee(파이프 + 파일 쓰기 우회)find -execfind -ok(임의 명령 실행)git configgit -cgit alias(설정 통한 실행)
경로 검증 — 다층 방어
is_path_allowed(path)
│
├─ 1. null byte 차단 (\0)
├─ 2. ".." 컴포넌트 차단 (경로 순회)
├─ 3. URL 인코딩 순회 차단 (..%2f)
├─ 4. ~user 형식 차단 (~root, ~nobody 등)
│ (단, ~ 와 ~/ 는 허용 — 자신의 홈)
├─ 5. 절대 경로이면:
│ workspace_dir 하위 → ✅
│ allowed_roots 하위 → ✅
│ workspace_only=true → ❌
│ (workspace_only=false → 아래 forbidden 검사로 계속)
└─ 6. forbidden_paths 접두사 검사
is_resolved_path_allowed(resolved: &Path) ← 캐노니컬 경로로 검증
→ 심볼릭 링크 탈출 방지용
→ workspace_dir.canonicalize() 기준 검사
우선순위 (중요):
workspace_dir > allowed_roots > forbidden_paths > workspace_only
즉, workspace 안에 있으면 forbidden_paths가 /home이어도 허용.
Rate Limiting — ActionTracker
pub struct ActionTracker {
actions: Mutex<Vec<Instant>>, // 최근 1시간 내 액션 타임스탬프
}
record() → 1시간 이전 항목 제거 → 현재 항목 추가 → 현재 카운트 반환
count() → 1시간 이전 항목 제거 → 현재 카운트 반환 (기록 없음)
enforce_tool_operation(Read, ...) → 항상 Ok
enforce_tool_operation(Act, ...) → !can_act() → Err
→ count > max_actions_per_hour → Err
기본 max_actions_per_hour = 20 — 꽤 보수적.
복잡한 작업 시 늘려야 함.
Tool과의 연결
// all_tools_with_runtime()에서 모든 도구에 Arc<SecurityPolicy> 주입
let security = Arc::new(SecurityPolicy::from_config(&config.autonomy, &workspace));
let tools = all_tools_with_runtime(Arc::new(config), &security, ...);
ShellTool 예시 (추정되는 패턴):
async fn execute(&self, args: Value) -> Result<ToolResult> {
let command = args["command"].as_str().unwrap();
let approved = args["approved"].as_bool().unwrap_or(false);
// 커맨드 허용 여부
self.policy.validate_command_execution(command, approved)?;
// 경로 인수 검사
if let Some(forbidden) = self.policy.forbidden_path_argument(command) {
return Err(format!("Forbidden path: {forbidden}"));
}
// Act 오퍼레이션 → rate limit 체크
self.policy.enforce_tool_operation(ToolOperation::Act, "shell")?;
// 실제 실행
...
}
config.toml 설정
[autonomy]
level = "supervised" # readonly / supervised / full
workspace_only = true # 기본값
max_actions_per_hour = 20 # rate limit
block_high_risk_commands = true
require_approval_for_medium_risk = true
# 추가 명령어 허용
allowed_commands = ["git", "npm", "cargo", "ls", "cat", "grep",
"curl", "docker"] # curl, docker 추가 예시
# workspace 외 추가 허용 경로
allowed_roots = ["/shared/data", "~/Desktop"]
# 채널(Telegram 등)에서 특정 도구 비활성화
non_cli_excluded_tools = ["shell", "file_write"]
내 로컬 프로젝트 권장 설정:
[autonomy]
level = "full" # 로컬 개발 환경이면 Full
workspace_only = true
max_actions_per_hour = 100 # 작업량에 따라
block_high_risk_commands = true
# curl 필요하면 명시적으로 추가
allowed_commands = ["git", "npm", "cargo", "ls", "cat", "grep",
"find", "echo", "pwd", "wc", "head", "tail",
"date", "python3", "pip"]
보조 보안 모듈 개요
SecretStore (secrets.rs):
config.toml 내 API key 암호화 저장
secrets.encrypt = true 설정 시 활성화
redact() — 로그 출력 시 앞 4자 + "***"
PromptGuard (prompt_guard.rs):
LLM 입력/출력에서 프롬프트 인젝션 패턴 감지
"Ignore previous instructions", "You are now..." 등
GuardAction::Block → 도구 실행 차단
LeakDetector (leak_detector.rs):
LLM 응답에서 민감 정보 유출 감지
API key 패턴, 자격증명 형식 검출
EstopManager (estop.rs):
긴급 정지 — 모든 에이전트 활동 즉시 중단
채널에서 특정 명령으로 트리거
Sandbox (Docker/Firejail/Bubblewrap/Landlock):
OS 레벨 프로세스 격리
shell 명령을 샌드박스 안에서 실행
detect::create_sandbox() — 환경에 맞는 백엔드 자동 선택
prompt_summary() — LLM에 정책 노출
policy.prompt_summary()
출력 예시:
**Autonomy level**: Supervised
**Workspace boundary**: file operations are restricted to `/home/user/project`.
**Allowed shell commands**: `git`, `ls`, `cat`. Commands not on this list will be rejected.
**Forbidden paths**: `/etc`, `~/.ssh`. Any read/write/exec targeting these paths will be blocked.
**High-risk commands** (rm, kill, reboot, etc.) are blocked.
**Medium-risk commands** require user approval before execution.
**Rate limit**: max 20 actions per hour.
이 내용이 system prompt에 자동으로 추가돼서 LLM이 차단될 명령을 사전에 알 수 있어.
관련
- channel-system — process_channel_message에서 SecurityPolicy 생성
- tool-system — all_tools_with_runtime()에 Arc 주입
- config-schema — [autonomy] 설정 섹션
- agent-loop — approval_manager와 연동
- overview — ZeroClaw 학습 지도