-
AI 에이전트에게 셸을 쥐여 주는 순간 — OpenClaw가 exec 실행과 승인 게이트로 위험을 길들인 방법IT 2026. 7. 15. 21:00
AI 에이전트가 "그냥 대화만 하는 챗봇"을 넘어 실제로 일을 하려면, 결국 어느 시점엔 셸 명령을 실행해야 한다. 빌드를 돌리고, 로그를 긁고, 파일을 옮기고, 테스트를 돌리는 일은 전부 셸에서 일어난다. OpenClaw는 오픈소스 개인 AI 어시스턴트인데, 이 능력을
exec라는 도구 하나로 제공한다 — 워크스페이스나 페어링된 노드(node, 게이트웨이에 연결된 별도 실행 호스트, 예: 맥 컴패니언 앱)에서 셸 명령을 실행하는 도구다. 그런데 셸을 쥐여 주는 순간 문제가 생긴다.rm -rf한 줄이면 파일이 날아가고, 잘못된 명령 하나가 시스템을 망가뜨린다. 에이전트가 실제로 유용하려면 명령 실행 능력이 필요하지만, 그 능력이 곧 시스템 위험이다. 이 글은 OpenClaw가 이 "유용성 ↔ 위험"의 균형을exec실행 능력과 실행 승인(approvals) 게이트 — 명령마다 허용(allow)·거부(deny)·물어보기(ask)를 정하는 정책 — 으로 어떻게 잡았는지를 따라간다.배경 — 에이전트가 일하려면 셸이 필요하다
OpenClaw의
exec는 단순히 "명령을 돌리는" 도구가 아니라, 파일을 만들고 고치고 지울 수 있는 변경 가능한(mutating) 셸 표면이다. 문서가 분명히 못 박는 점이 하나 있다 — OpenClaw의 파일 도구(write·edit·apply_patch)를 전부 꺼도exec가 읽기 전용이 되지는 않는다.exec안에서rm이나 리다이렉션을 쓰면 그대로 파일이 바뀌기 때문이다. 즉exec는 에이전트가 가진 권한 중 가장 강력하면서 가장 위험한 도구다.가장 기본적인 호출은 이렇게 생겼다.
{ "tool": "exec", "command": "ls -la" }코드 설명. 이건 문서의 가장 단순한 예시로, 셸에서
ls -la를 돌려 디렉터리 목록을 받는 호출이다. 핵심 메시지는 "에이전트가 도구를 부르는 방식은 평범한 JSON 한 줄"이라는 점이다.tool로 어떤 도구를 쓸지,command로 어떤 셸 명령을 돌릴지 적는다. 이 단순함이 곧 위험의 출발점이다 —command자리에ls -la대신rm -rf ~가 들어가도 형태는 똑같이 평범한 JSON 한 줄이기 때문이다. 그래서 "무엇을 실행하느냐"를 형태만 보고 판단할 수 없고, 별도의 정책 계층이 필요해진다.exec는 여러 실행 위치(host)를 고를 수 있다. 어디서 명령이 도느냐에 따라 위험의 성격이 달라지므로, 먼저 이 갈래를 보자.다이어그램 설명. 위 다이어그램은
exec의 기본 라우팅 전략인host=auto가 어디서 명령을 돌릴지 어떻게 정하는지를 보여준다. 첫 판단은 "지금 이 세션에 샌드박스 런타임이 켜져 있는가"이다. 켜져 있으면 격리 컨테이너 안에서 돌고, 꺼져 있으면 실제 게이트웨이 호스트에서 돈다. 점선으로 표시된 노드 경로는 호출자가 명시적으로 지정했을 때만 가는 길이다. 이렇게 위치를 나눈 이유는 위험의 크기가 다르기 때문이다 — 격리 컨테이너 안의 명령은 망가져도 컨테이너만 영향받지만, 게이트웨이나 노드의 명령은 진짜 사용자 파일과 시스템을 건드린다. 놓치기 쉬운 함정은 샌드박싱이 기본적으로 꺼져 있다는 점이다 — 그래서 별다른 설정이 없으면host=auto는 격리가 아니라 실제 게이트웨이 호스트로 떨어진다.host는 호스트네임 선택기가 아니라auto·sandbox·gateway·node네 가지 중 하나만 받는 라우팅 키라는 점도 함께 기억해야 한다.그래서 무슨 가치인가. 에이전트가 빌드·테스트·파일 정리 같은 진짜 작업을 하려면 셸이 필수지만, 그 셸이 어디서 도느냐에 따라 사고의 반경이 달라진다.
exec는 "실행 능력"과 "실행 위치"를 분리해, 위험을 통제할 첫 손잡이를 만든다.핵심 문제 — 강력한 실행과 시스템 안전은 정면으로 충돌한다
문제를 정확히 짚으면 이렇다. 에이전트에게 셸을 주되, 위험한 명령은 막아야 한다. 그런데 이 둘은 단순한 on/off로 풀리지 않는다. 구체적인 실패 시나리오로 나눠 보자.
첫째, 완전히 막으면 에이전트가 무력해진다. 셸 실행을 통째로 꺼 버리면 안전하긴 하지만, 그 순간 에이전트는 빌드 하나 못 돌리는 대화 상자가 된다. 개인 AI 어시스턴트를 직접 운영하는 이유 자체가 "내 컴퓨터에서 실제로 일을 시키는 것"인데, 그 핵심을 잃는다.
둘째, 전부 열면 한 줄 사고가 시스템을 망가뜨린다. 반대로 모든 명령을 묻지 않고 실행하면(이른바 YOLO 모드), 에이전트가 헷갈려 만든
rm -rf나, 외부 채널로 들어온 악의적 지시 한 줄이 그대로 파일을 날린다.셋째, 매번 물어보면 사람이 지친다. 그렇다고 모든 명령마다 사람에게 "이거 돌려도 돼?"라고 물으면,
ls한 번 돌리는 데도 승인 버튼을 눌러야 한다. 승인 피로(approval fatigue)가 쌓이면 사람은 결국 내용을 안 보고 다 승인하게 되고, 그 순간 게이트는 있으나 마나 한 장식이 된다.이 세 길을 한 축에 놓고 비교하면, 왜 중간 지대가 필요한지 분명해진다.
다이어그램 설명. 위 다이어그램은 셸 실행 정책의 세 극단과, 그것들이 모두 같은 결론으로 수렴함을 보여준다. "전부 막기" 가지는 안전하지만 에이전트를 무력화하고, "전부 열기" 가지는 강력하지만 한 줄 사고에 무방비이며, "매번 묻기" 가지는 안전하지만 사람을 지치게 한다. 세 극단이 모두 "중간 지대 — 안전한 명령은 자동으로 허용하고 위험한 것만 게이트한다"로 모이는 게 핵심 메시지다. 이 패턴을 고른 이유는 위험과 빈도가 명령마다 다르기 때문이다.
ls는 위험이 거의 없으면서 매우 자주 쓰이고,rm -rf는 드물지만 치명적이다. 둘을 같은 정책으로 다루면 어느 쪽이든 손해를 본다. 함정은 이 중간 지대를 "그냥 한 번 물어보고 끝"으로 오해하는 것 — 뒤에서 보듯 OpenClaw는 정책·허용목록·사람 승인의 세 겹이 모두 동의해야 명령이 도는 구조를 쓴다.그래서 무슨 가치인가. 위험과 유용성은 한 다이얼로 맞바꿀 수 없다. 안전한 다수와 위험한 소수를 다르게 다뤄야, 에이전트는 평소엔 막힘없이 일하면서도 치명적 순간엔 멈춘다.
해결 방법 — 위치 라우팅 + 모드 정책 + 허용목록 + 사람 승인
OpenClaw의 답은 단일 스위치가 아니라 겹겹의 게이트다. 핵심은 네 축이다 — 어디서 돌릴지(host), 어떤 정책으로 다룰지(mode), 무엇을 자동 허용할지(allowlist + safe bins), 그리고 막히면 어떻게 사람에게 물을지(approvals).
축 1 — 정책 모드: deny · allowlist · ask · auto · full
호스트 실행의 정책은
tools.exec.mode라는 하나의 정규화된 손잡이로 정해진다. 다섯 단계가 있고, 위험을 받아들이는 정도가 점점 커진다.다이어그램 설명. 위 다이어그램은 호스트 셸 실행의 다섯 가지 정책 모드를 위험 수용도 순으로 늘어놓은 것이다. "deny(모두 차단)"는 어떤 호스트 명령도 돌지 못하게 하고, "allowlist(허용목록)" 모드는 미리 신뢰한 명령만 묻지 않고 돌리며 나머지는 그냥 막는다. "ask(묻기)" 모드는 허용목록에 없는 명령(미스)이 나오면 사람에게 승인을 요청한다. 그다음 "auto(자동)" 모드는 한 걸음 더 나아가, 명백히 안전한 결정적(deterministic) 매치는 바로 실행하고, 애매한 나머지만 OpenClaw 내장 자동 리뷰어에게 먼저 보낸 뒤 그래도 미심쩍으면 사람에게 넘긴다. "full(전부 실행)"은 승인 없이 모두 돌리는 YOLO 모드다. 이렇게 다섯 단계로 쪼갠 이유는 사용자마다 위험 감수 수준이 다르기 때문이다 — 같은 어시스턴트라도 격리된 실험 환경에선 느슨하게, 진짜 개인 서버에선 빡빡하게 둘 수 있다. 놓치기 쉬운 함정은 묻기와 자동의 차이다. 묻기는 미스마다 무조건 사람을 부르지만, 자동은 사람을 부르기 전에 LLM 기반 자동 리뷰어가 한 겹 걸러서 사람의 부담을 줄인다.
축 2 — 허용목록과 safe bins: 안전한 다수를 자동 통과
승인 피로를 줄이는 핵심이 허용목록(allowlist)이다. 미리 신뢰한 명령은 묻지 않고 통과시킨다. 허용목록 항목은 에이전트별로 따로 관리되고, 바이너리의 절대 경로 글로브(예:
/opt/homebrew/bin/rg)나 명령 이름 글로브(예:rg)로 매칭한다.{ "version": 1, "agents": { "main": { "allowlist": [ { "pattern": "python3", "argPattern": "^safe\\.py$" } ] } } }코드 설명. 이 예시는
python3 safe.py는 허용하되python3 other.py는 허용목록 미스로 처리하는 항목이다.pattern은 어떤 바이너리를 신뢰할지,argPattern은 그 바이너리에 어떤 인자 모양만 허용할지를 정규식으로 제한한다(실행 파일 토큰은 빼고 나머지 인자에 대해 매칭). 핵심 메시지는 "인터프리터를 통째로 신뢰하지 말고, 특정 인자 모양까지 좁혀서 신뢰하라"는 것이다. 이 패턴을 고른 이유는python3같은 인터프리터를 무조건 허용하면 사실상 임의 코드 실행을 허용하는 셈이기 때문이다.축 3 — 사람 승인 게이트: 막히면 어떻게 물을까
허용목록을 빠져나간 명령은 사람 승인 게이트로 간다. 이 실행 승인(exec approvals)은 샌드박스 안의 에이전트가 실제 호스트에서 명령을 돌리려 할 때 거치는 안전 인터록(interlock, 모든 조건이 동의해야 작동하는 잠금 장치)이다. 승인이 필요한 명령이 들어오면,
exec도구는 즉시 결과를 돌려주지 않고 "승인 대기(approval-pending)" 상태와 승인 ID를 반환한 뒤 기다린다.다이어그램 설명. 위 다이어그램은 허용목록을 통과하지 못한 명령이 사람 승인을 거치는 전체 흐름을 보여준다. 요청이 들어오면 게이트웨이가 운영자(Control UI·맥 앱·또는 Slack/Telegram 같은 채널)에게 승인 요청을 뿌린다. 그다음 단계가 이 설계의 핵심이다 — 명령·작업 디렉터리·정확한 인자 목록·환경·구체적 파일 하나를 묶은 표준 실행 계획(canonical plan)을 미리 고정한다. 운영자가 승인하면 이 고정된 계획 그대로 실행되고, 거부하거나 타임아웃 되면 명령은 아예 돌지 않는다. "요청 내용이 승인 후 바뀌면 불일치로 거부" 가지가 특히 중요하다 — 승인 요청을 만든 뒤 호출자가 명령이나 디렉터리를 슬쩍 바꾸면, 게이트웨이는 그 불일치를 잡아내 실행을 거부한다. 이 패턴을 고른 이유는 비동기 승인에는 시간차가 있기 때문이다. "사람이 본 명령"과 "실제로 도는 명령"이 그 시간차 사이에 달라지면 승인이 무의미해지므로, 본 그대로만 돌도록 계획을 못 박는다. 함정은 승인이 파일 내용까지 묶는다는 점이다 — 스크립트를 승인받은 뒤 실행 전에 그 파일을 고치면, 바뀐 내용은 승인되지 않은 것이라 실행이 막힌다.
그래서 무슨 가치인가. 세 축은 서로 다른 시점에 서로 다른 위험을 막는다. 위치 라우팅은 사고의 반경을, 모드 정책은 전체 위험 수준을, 허용목록은 빈번한 안전 명령의 마찰을, 사람 승인은 치명적 소수의 마지막 선을 각각 맡는다. 한 덩어리 "안전 모드"였다면 어느 하나도 제대로 못 막았을 것이다.
결과 — 막힘 없이 일하되 치명적 순간엔 멈춘다
이 설계가 사용자·에이전트·시스템에 주는 실제 가치를 정리하면 다음과 같다.
- 에이전트가 실제로 일한다. 허용목록과 자동 모드의 결정적 매치 덕분에, 빌드·테스트·검색 같은 빈번한 안전 명령은 사람을 부르지 않고 바로 돈다. 셸을 통째로 막았을 때의 "무력한 챗봇"을 피한다.
- 치명적 명령엔 사람이 끼어든다. 허용목록을 벗어난 명령은 승인 게이트로 가서, 사람이 정확히 무엇이 도는지 본 뒤에만 실행된다. 승인 후 명령이 바뀌면 불일치로 거부되므로, "본 것"과 "도는 것"이 항상 일치한다.
- 위험 반경이 위치로 나뉜다. 샌드박스가 켜져 있으면 사고가 컨테이너 안에 갇히고, 게이트웨이·노드 실행만 진짜 시스템을 건드린다. 다만 샌드박싱은 기본 꺼짐이라, 격리를 원하면 명시적으로 켜야 한다는 점은 운영자가 기억해야 한다.
마지막으로 짚을 점은, 이 모든 게 겹쳐 쌓인(stack) 정책이라는 것이다. 설정과 호스트 승인 파일 중 더 엄격한 쪽이 이기므로, 진짜로 "묻지 않고 다 실행"하려면 여러 계층을 의도적으로 함께 열어야 하고, 하나만 빡빡해도 전체가 빡빡해진다 — 안전한 기본값으로 기울어 있는 설계다.
핵심을 한 줄로 요약하면 이렇다. 에이전트에게 셸을 쥐여 주는 것은 곧 시스템 위험을 떠안는 일이지만, OpenClaw는 그 위험을 단일 다이얼로 맞바꾸는 대신 — 위치·정책·허용목록·사람 승인이라는 겹겹의 게이트로 쪼개 — 안전한 다수는 마찰 없이 흘려보내고 치명적 소수만 멈춰 세운다. 개인이 AI 어시스턴트에게 더 많은 일을 맡길수록, 이 "실행 능력과 승인 게이트의 균형"이 유용한 조수와 위험한 사고 사이를 가른다.
이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.
'IT' 카테고리의 다른 글
지금 시키는 일만 하던 에이전트가 미래의 자기에게 일을 거는 법 — OpenClaw cron 도구 (1) 2026.07.16 세션이 끝나면 다 잊어버리는 AI — OpenClaw가 memory 도구로 의미 기반 기억을 만든 방법 (0) 2026.07.16 큰 작업을 혼자 떠안지 않는 에이전트 — OpenClaw가 하위 작업을 격리 세션으로 떼어내는 법 (0) 2026.07.16 에이전트가 답만 뱉던 시절은 끝났다 — OpenClaw가 message 도구로 능동 발신과 채널 액션을 연 방법 (0) 2026.07.15 LLM에게 파일을 통째로 다시 쓰게 두면 안 되는 이유 — OpenClaw가 파일 편집을 4개 도구로 쪼갠 방법 (0) 2026.07.15 fetch로는 로그인·동적 페이지를 못 읽는다 — OpenClaw가 격리 브라우저로 푼 방법 (0) 2026.07.14 페이지 한 장 읽자고 브라우저를 통째로 띄울 건가 — OpenClaw가 web_fetch로 본문만 가볍게 긁는 방법 (0) 2026.07.14 LLM은 어제 일을 모른다 — OpenClaw가 에이전트에 웹 검색을 붙인 방법 (0) 2026.07.14 에이전트에 외부 도구를 붙이는 콘센트 — OpenClaw가 MCP 서버를 등록·관리하는 방법 (0) 2026.07.13 설정 파일 하나가 거대한 JSON 덩어리일 때 — OpenClaw가 오타·스키마 위반·비밀 노출을 막은 방법 (0) 2026.07.13