새기능 #694
open
conduct-web: tmux 세션 오케스트레이터 웹UI (외부 노출 + 재사용화)
Added by joungmin ko 29 days ago.
Updated 22 days ago.
Description
h2. 목적
macmini의 tmux 세션들(대부분 원격/로컬 Claude Code 인스턴스)을 한 웹UI에서 진행/관리. headless claude 경유 조작 + 다이렉트 send-keys.
h2. 현황 (as-built, 2026-07-20)
- web.py (Python stdlib, SSE 스트리밍, Basic Auth) — pm2 앱 conduct-web, 0.0.0.0:8787
- 대화창(headless claude가 tmux 조작) + pane 실시간 미리보기 + 다이렉트 send-keys/빠른키
- 대화/조작 로그 영속화 (conduct.history.jsonl / conduct.log / conduct.jsonl)
- 부팅 자동시작: pm2 (ecosystem, PATH 고정) + pm2 save
- 외부 노출: https://remotework.cloud-handson.com (nginx .147 -> .109:8787, TLS certbot, 앱 Basic Auth)
h2. 남은 작업
- 다른 머신에서도 쓸 수 있게 재사용화 (포터블 패키징 / 호스트별 설정 분리)
h2. 추적성
- 설계서: docs/design/conduct-web/README.md
- 구현: hosts/macmini/pm2/{services.md,ecosystem.config.js}, hosts/i5-8500/nginx/remotework.cloud-handson.com.conf
- Status changed from 신규 to 진행
설계서·인벤토리·외부노출 현행화 커밋: joungmin/homelab-ops @ 5ada076
- docs/design/conduct-web/README.md (설계서 신규, as-built + 재사용화 계획)
- hosts/macmini/pm2 (services/ecosystem), hosts/i5-8500/nginx (remotework vhost)
다음: 다른 머신 재사용화(설정 외부화/이식).
세션 화면 스냅샷 + 대화이력 Oracle ADB 저장/조회 추가 (커밋 d620c98).
구현:
- 전용 스키마 conduct (snapshot/turn). TUI라 tmux 스크롤백이 없어 30s 주기 캡처(sha256 dedup)만이 과거 보존 수단.
- web.py DB 계층: 스냅샷 스레드, 턴 적재, /api/snapshots·/api/snapshot·/api/search·/api/history(DB 우선, 파일 폴백).
- 웹 UI: pane "기록"(세션별 타임라인) + 사이드바 전문검색.
- thin 모드 config_dir(cwallet.sso, 월렛 비번 불필요). .dbenv(600, 미커밋). DB 불가 시 graceful degrade.
검증: 스냅샷 dedup 적재, 타임라인, 한글 전문검색(snapshot+turn), history 백필, 도메인 경유, pm2 부팅 DB 연결.
문서: 설계서 docs/design/conduct-web/README.md 갱신 + schema.sql 추가.
남은 것(Open Questions): 스냅샷 볼륨 관리(near-dup 억제/프루닝), 설정 외부화·이식(#694 재사용화).
이식형 패키징 + 스냅샷 볼륨관리 완료 (커밋 0544b12, push 됨).
재사용화(다른 PC 설치형):
- 설정 외부화 conduct.conf(HOST/PORT/HOST_ID/…), 없으면 안전 기본값(HOST=127.0.0.1).
- install.sh: web.py 배치 + conf/.auth 생성 + claude/tmux 경로탐지해 pm2 ecosystem 생성 + 등록. 임시 디렉터리로 검증(무인증 401 / 인증 200 / SQLite 자동생성).
- repo 추적 소스: conduct/(web.py, install.sh, conduct.conf.example, README, schema.sql).
DB 자동선택: .dbenv 있으면 Oracle, 없으면 SQLite(stdlib) 폴백. 두 백엔드 동일 인터페이스. host_id 태깅으로 여러 PC 가 한 ADB 공유해도 격리.
스냅샷 볼륨: 원인은 애니메이션 글리프(⏺/●) 토글(실측). 정규화 후 해시 dedup + 세션당 300 프루닝. 백로그 166건 정리(179->13).
남은 것: 인증/권한 강화, 다중 호스트 집계 UI, 볼륨 2차(타이머 라인 정규화/보관기간 프루닝).
기록 흐름 뷰 + live 스크롤백 + 저장 중복정리 (커밋 0f1ec71, push 됨).
- 기록: "기록" 열면 바로 연속 흐름 뷰(/api/flow). 범위/간격 샘플링/날짜그룹. 기본 증분(변경만) → 인접 스냅샷 겹침(76~95%) 반복 제거(toss 378→135줄). 전체화면 토글, 목록 부가.
- live 스크롤: capture-pane -S -3000 로 스크롤백 포함. ssh(vpd) 55→1170줄 스크롤. 풀스크린 TUI 는 tmux 스크롤백이 없어 현재화면만(구조적) → 과거는 기록으로.
- 저장 중복: 정규화 배포 전/후 해시 혼재로 생긴 완전-중복 13건 삭제(35→22) + 저장 해시 정규화 통일(재발 방지).
남은 것: 인증/권한 강화, 다중 호스트 집계 UI, 흐름 증분 정밀화(chrome 라인 억제).
4관점 리뷰(개발자/설계자/사용자/보안) 후 보안 1차 조치 (커밋 78c0298, push 됨).
리뷰 요약:
- 보안 등급 Critical(인터넷 노출). 단일 Basic Auth = 전 tmux 세션 RCE 라는 단일 실패점.
- 확정 버그: conf 값을 리터럴이 덮어써 SNAP_INTERVAL/MAX_SNAP/PRUNE 튜닝 무력화(데드코드).
적용(공개 노출 유지 결정):
- 데드코드 제거 → conf 튜닝 복원.
- CSRF: 상태변경 POST 에 Origin/Referer 호스트 == Host 검증. 교차사이트 403(로컬/도메인 검증). 무Origin(비브라우저) 허용.
- 인증 레이트리밋: IP별 실패 10회/5분 초과 429. XFF/X-Real-IP 는 신뢰프록시(nginx .147)에서만 신뢰 → LAN 직접접근 헤더위조 우회 차단.
미해결(우선순위 순, 12장 추적):
- [권한] allowedTools Bash(tmux:*) 는 tmux run-shell/new-window 로 셸 이스케이프 → 서버측 명령 화이트리스트 필요.
- [권한] /api/send·/api/key 직접 RCE, 프롬프트 인젝션(pane→claude→send-keys).
- [노출] 스냅샷이 전 세션 화면 평문 저장 → 민감세션 제외/마스킹.
- [UX] 모바일 반응형 없음, 위험키 확인/피드백 없음.
- 대안: 공개 제거 시 Tailscale/WireGuard(테더링에도 IP무관) — 사용자는 공개 유지 선택.
셸 이스케이프 차단 완료 (A+B, 커밋 39880f6, push 됨).
문제: allowedTools "Bash(tmux:*)" 는 tmux run-shell/new-window 등으로 임의명령 실행 가능(샌드박스 아님).
조치:
- B) tmux-safe 래퍼: capture-pane/send-keys/list-* 만 허용, run-shell/new-window/split-window/if-shell/pipe-pane 및 포맷 셸실행 #(...) 거부. 실제 tmux 는 CONDUCT_REAL_TMUX 절대경로로 exec(셸 미경유).
- A) claude allowedTools 를 Bash(tmux-safe :*) 로 축소 + SYS 가 tmux-safe만 쓰게 + 프롬프트인젝션 경고.
- PATH 격리: claude 서브프로세스 PATH 에서 실제 tmux 디렉터리 제외 → 체이닝으로 실제 tmux 호출도 차단.
실증(마커파일 미생성 확인): tmux run-shell(승인요구→거부), tmux-safe run-shell(래퍼 exit2), ; 체이닝(거부), $() 치환(미실행), | tee(권한게이트 거부), > 리다이렉트(차단), -F #(id)(래퍼 exit3). 정상 capture-pane/send-keys 동작. settings.json 광범위 Bash allow/bypass 없음 확인.
남은 보안(12장): /api/send·key 직접 RCE(사용자 주도), 프롬프트 인젝션(경고만), 스냅샷 평문 비밀 저장, UX 반응형.
설정 환경변수화 + 공개 저장소 배포 + 종합 보안테스트 (homelab-ops 커밋 0679bc9).
- 환경변수화: CONDUCT_ > conduct.conf > 기본값(cfg()). 로그인 CONDUCT_USER/PASS, DB ORACLE_* env 지원. 코드에 사이트값 하드코딩 제거(검증: CONDUCT_PORT/SNAP_INTERVAL/USER/PASS 오버라이드 우선 적용).
- 일반화: TRUSTED_PROXIES 기본값에서 내부 IP 제거(macmini는 conf에 192.168.0.147 명시).
- 공개 배포: https://github.com/devmrko/tmux-conduct (MIT, public). conduct/ 를 사이트값 제거+환경변수화, 영문/한글 README(한글 기본, README.en.md 영문) + LICENSE + .gitignore 보강. 전수 스캔으로 비공개정보 0 확인.
- 종합 보안테스트(2026-07-27) 전부 PASS: 인증(401/429), CSRF(교차Origin 403), SQL/명령 주입 무해, 실배포 셸이스케이프 미생성, git 히스토리 시크릿 0.
잔여(12장): 다이렉트 send-keys(설계상 본질), 스냅샷 평문 비밀, 프롬프트 인젝션, UX 모바일 반응형.
정적분석(SAST) 보안 스캔 실행 + 결과 공개 (homelab-ops 42d259f, 공개 repo 580da48).
툴/결과(2026-07-27, github devmrko/tmux-conduct):
- Bandit 1.8.6: 6 Low / 0 Medium / 0 High (subprocess 사용 알림 + graceful-degrade try/except/pass).
- Semgrep 1.136.0: 236룰(python·security-audit·command-injection·secrets) 0건.
- gitleaks 8.30.1: 0(git 히스토리 포함). shellcheck 0.11.0: 0.
- CodeQL: 2건 py/command-line-injection(critical) → 리스트인자+shell=False 라 명령주입 불가(오탐), 오탐 사유 기재해 dismiss. 열린 경보 0.
조치:
- 인자주입 방어: /api/send·/api/key send-keys 에 -- 추가(값이 -로 시작해도 옵션 오해 차단, 리터럴 -X 전송 확인).
- CI: .github/workflows/security.yml 로 push/PR/주간마다 Bandit+Semgrep+gitleaks+CodeQL 자동 실행. 최신 run success.
- 결과 문서: 공개 repo SECURITY.md + README(한/영) 보안점검 섹션.
SAST 미커버(설계상, 12장 추적): 다이렉트 send-keys 본질, 스냅샷 평문, 프롬프트 인젝션.
Also available in: Atom
PDF