📊6개 대시보드 종류 (전체 안내)
시스템에는 웹으로 접근하는 6개의 관리·조회 인터페이스가 있습니다. 아래 표에서 한눈에 비교한 뒤, 각 항목의 상세 사용법을 확인하세요.
한눈에 보기
| # | 대시보드 | 포트 | 주요 기능 | 접속 방법 |
|---|---|---|---|---|
| 1 | 🤖 OpenWebUI | :3000 | 메인 채팅 UI · 관리자 설정 · API Key 발급 | ✅ 브라우저에서 직접 접속 |
| 2 | 📞 AI 전화비서 | :5000/dashboard | 통화 기록·AI 요약·녹음 파일·PDF 보고서 | 채팅 질문 또는 SSH 터널 |
| 3 | 💬 Telegram 봇 관리 | :8445/dashboard | 세션·사용자·Tool·로그·공지·통계 | SSH 터널 또는 Windows 포트포워딩 |
| 4 | 🕸️ Browser Agent API | :8001/docs | 브라우저 작업·스크린샷·모니터링 API 문서 | ✅ 로컬에서 직접 접속 |
| 5 | 🗄️ Qdrant DB | :6333/dashboard | RAG 벡터 DB 저장 현황 | ✅ 로컬에서 직접 접속 |
| 6 | 📖 RAG API 문서 | :8000/docs | OpenAPI 엔드포인트 목록·테스트 | ✅ 로컬에서 직접 접속 |
127.0.0.1에 바인딩되어 외부에서 직접 접근 불가합니다. SSH 터널 또는 포트포워딩으로 접속합니다. 나머지(:3000·:8001·:6333·:8000)는 로컬/서버에서 바로 열 수 있습니다.
1️⃣ OpenWebUI 메인 (:3000)
접속: 브라우저에서
http://서버IP:3000로그인: 설치 시 입력한 관리자 이메일/비밀번호
API Key 발급 (Telegram 브릿지 설치에 필요):
- 좌측 하단 사용자 아이콘 → 설정
- 계정 탭 → API Keys 섹션
- 새 API Key 생성 → 키 복사 후 안전한 곳에 저장
2️⃣ AI 전화비서 대시보드 (:5000)
통화 기록, AI 요약, 녹음 파일, PDF 보고서를 조회합니다. 두 가지 방법으로 엽니다.
방법 1: OpenWebUI / Telegram 채팅창 (가장 간단)
👤 통화 기록 보여줘👤 최근 통화 목록 알려줘👤 녹음 파일 목록 보여줘👤 PDF 보고서 목록 보여줘※ 전화 어시스턴트·통화 녹음 관리·PDF 보고서 관리 Tool이 활성화되어 있어야 합니다.
방법 2: 단축 명령어 (터미널 — 최초 1회 등록)
# 단축 명령어 등록 (최초 1회)
echo 'alias dashboard="docker exec twilio-bot curl -s http://127.0.0.1:5000/dashboard > /tmp/dashboard.html && explorer.exe \"$(wslpath -w /tmp/dashboard.html)\""' >> ~/.bashrc
source ~/.bashrc
# 이후부터 이것만 입력
dashboard
cd ~/OpenWebUI/twilio-bot/data/recordings/ && explorer.exe . — 녹음 파일 폴더cd ~/OpenWebUI/twilio-bot/data/reports/ && explorer.exe . — PDF 보고서 폴더
3️⃣ Telegram 봇 관리 대시보드 (:8445)
세션·사용자·Tool·로그·통계를 브라우저에서 실시간으로 확인합니다. 로컬 전용(127.0.0.1)이므로 SSH 터널 또는 포트포워딩으로 접속합니다.
대시보드 탭 구성
| 탭 | 주요 내용 | 자동 갱신 |
|---|---|---|
| 📊 개요 | 활성 세션 수·메시지 수·차단 수·가동시간·OpenWebUI 연결 상태 | 15초 |
| 👥 세션 | 접속 중인 사용자 목록 — User ID·모델·메시지 수·Tool 수·마지막 활동 | 15초 |
| 👤 사용자 | 허용 사용자 추가/제거·차단 목록 관리·차단 해제 | 수동 |
| 🔧 Tool | 등록된 Tool 목록·세션별 사용 현황·전체 ON/OFF | 수동 |
| 📋 로그 | 줄 수 지정(최대 500)·10초 자동 새로고침 토글 | 선택 |
| 📢 공지 | 모든 활성 사용자에게 메시지 전송 | — |
🔑 인증 토큰 확인
# 서버에서 토큰 확인
grep INTERNAL_API_SECRET ~/telegram-openwebui-bridge/.env
한 번 입력 후 브라우저에 자동 저장됩니다. Telegram에서는 /admin → 웹 대시보드 버튼으로 토큰 앞부분을 확인할 수 있습니다.
🖥️ 접속 방법 1 — SSH 터널 (보안 권장)
ssh -L 8445:localhost:8445 user@서버IP
2단계: Windows 브라우저에서 접속
http://localhost:8445/dashboard
🖥️ 접속 방법 2 — Windows 포트포워딩 (WSL2 전용)
127.0.0.1:8445가 WSL2 내부 루프백이라 Windows 브라우저에서 직접 접근이 안 됩니다.PowerShell(관리자)에서 포트포워딩을 설정하면 SSH 터널 없이 접속 가능합니다.
PowerShell (관리자)에서 실행:
# WSL2 IP 확인 후 포트포워딩 설정
$wslIP = (wsl hostname -I).Trim().Split()[0]
netsh interface portproxy add v4tov4 listenport=8445 listenaddress=127.0.0.1 connectport=8445 connectaddress=$wslIP
# 설정 확인
netsh interface portproxy show all
이후 Windows 브라우저에서:
http://localhost:8445/dashboard
netsh interface portproxy delete v4tov4 listenport=8445 listenaddress=127.0.0.1
🖥️ 접속 방법 3 — docker-compose 포트 변경 (가장 간단)
0.0.0.0으로 변경하면 Windows 브라우저에서 바로 접속 가능합니다.
cd ~/telegram-openwebui-bridge
sed -i 's/127.0.0.1:8445:8445/0.0.0.0:8445:8445/g' docker-compose.yml
docker compose restart
이후 Windows 브라우저에서 http://localhost:8445/dashboard 접속.📱 Telegram 명령어로 대신하기
| 웹 탭 | Telegram 명령어 |
|---|---|
| 📊 개요 | /admin → 현황 | /stats | /status |
| 👥 세션 | /users |
| 👤 사용자 | /adduser | /block | /unblock |
| 🔧 Tool | /tools |
| 📋 로그 | /logs | /logs 50 |
| 📢 공지 | /broadcast 메시지 |
4️⃣ Browser Agent API 문서 (:8001/docs)
API 문서 접속:
http://localhost:8001/docs — 전체 엔드포인트 목록과 직접 테스트기본 주소:
http://localhost:8001인증: 모든 요청에
Authorization: Bearer {BROWSER_AGENT_API_KEY} 헤더 필요# API Key 확인
grep BROWSER_AGENT_API_KEY ~/OpenWebUI/.env
# 헬스 체크
curl http://localhost:8001/health
# 브라우저 작업
curl -X POST http://localhost:8001/browse \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"task":"오늘 날씨 알려줘"}'
/browse · /browse/stream · /browse/batch · /screenshot · /history · /monitors · /memory 등. 전체 목록과 상세 예시는 브라우저 에이전트 → API 레퍼런스를 참고하세요.
5️⃣ Qdrant 벡터 DB 대시보드 (:6333)
대시보드 접속:
http://localhost:6333/dashboard (로컬에서 직접)컬렉션 조회 (터미널):
# 저장된 컬렉션 목록 확인
curl -s http://localhost:6333/collections
nomic-embed-text 모델로 생성되어 Qdrant에 저장됩니다. RAG 사용법 상세는 아래 RAG 섹션을 참고하세요.
6️⃣ RAG / Tools API 문서 (:8000/docs)
API 문서 접속:
http://localhost:8000/docs (로컬에서 직접)상태 확인 (터미널):
# Tools 서버 헬스 체크
curl -s http://localhost:8000/health
http://localhost:8000/upload
📤 업로드 폴더 권한 설정 (업로드가 안 될 때)
:8000/upload)에서 파일 저장이 실패하면, 대부분 공유 폴더(~/ai-share)의 소유권이 컨테이너 사용자와 맞지 않는 것이 원인입니다. 컨테이너는 UID 1002로 동작하므로, 호스트의 공유 폴더 소유권을 1002:1002로 맞춰주면 됩니다.① 공유 폴더 소유권 변경:
# 마운트된 공유 폴더 소유권을 컨테이너 사용자(1002)로 변경
sudo chown 1002:1002 /home/<사용자>/ai-share
② 하위 폴더까지 적용 (단, 읽기전용 마운트는 제외):
# 하위 폴더(예: photos/홍길동)에도 저장한다면
# — 단, 읽기전용 마운트(recordings·reports)는 제외
sudo find /home/<사용자>/ai-share -mindepth 1 -maxdepth 1 \
! -name recordings ! -name reports -exec chown -R 1002:1002 {} +
③ 적용 확인:
# 소유자가 1002로 보이면 정상
docker exec openwebui-openapi-tools-1 ls -ld /app/media
~/OpenWebUI/twilio-bot/data/recordings/)과 PDF 보고서(~/OpenWebUI/twilio-bot/data/reports/)는 읽기전용(:ro)으로 마운트되므로 소유권을 바꾸면 안 됩니다. ②번 명령은 이 둘을 자동으로 건너뜁니다.
📚RAG 문서 검색
PDF 문서를 업로드하면 AI가 내용을 벡터로 변환하여 Qdrant에 저장합니다. 이후 채팅이나 전화에서 질문하면 문서에서 관련 내용을 찾아 답변합니다.
동작 원리
② 질문 → 질문을 벡터 변환 → Qdrant에서 유사 문서 검색 → AI가 참고하여 답변 생성
📄 PDF 업로드 방법
방법 1: 채팅창에서 업로드 (권장)
👤 이 PDF에서 환불 정책 찾아줘 (파일 첨부)👤 업로드한 문서에서 계약 조건 알려줘※ RAG 문서 검색 Tool이 모델에 할당되어 있어야 합니다.
방법 2: API로 업로드
# PDF 업로드
curl -X POST http://localhost:8000/documents/upload \
-F "file=@문서.pdf"
# 업로드 결과 예시
# {"status":"indexed","filename":"문서.pdf","chunks":15,"total_chars":12340}
🔍 문서 검색 방법
채팅창에서 검색
👤 환불 규정이 어떻게 되나요?👤 배송 기간은 며칠인가요?👤 계약 해지 조건 알려줘AI가 업로드된 PDF에서 관련 내용을 찾아 답변합니다.
📞 수신전화에서 RAG 활용
예시: 고객이 전화해서 "환불 정책이 어떻게 되나요?" → AI가 PDF에서 환불 정책을 찾아 음성으로 답변
※ 안부전화(발신)에서는 속도 최적화를 위해 RAG 검색이 비활성화되어 있습니다. 수신전화에서만 작동합니다.
📁 저장 위치
| 항목 | 경로 |
|---|---|
| 업로드된 PDF 원본 | ~/OpenWebUI/openapi-tools/data/ |
| 벡터 데이터 (Qdrant) | ~/OpenWebUI/qdrant/ |
| 임베딩 모델 | Ollama nomic-embed-text |
🗂️미디어 관리 · 업로드
문서(RAG), 통화 녹음·보고서, 브라우저 결과물 등 종류별로 업로드 경로와 저장 위치가 다릅니다. 업로드는 모두 파일명 정제·크기 제한·형식 검증을 거칩니다.
1. RAG 문서 업로드 (PDF·TXT·CSV·DOCX)
지식 기반 답변에 쓰일 문서는 두 가지 방법으로 올립니다. 일반 사용은 OpenWebUI 화면에서 끌어다 놓으면 되고, 자동화·API 연동 시에는 OpenAPI 도구 서버의 업로드 엔드포인트를 직접 호출합니다. 업로드하면 텍스트 추출 → 스마트 청킹 → Qdrant 벡터 색인까지 자동 진행되며, 같은 파일은 기존 벡터를 지우고 다시 색인합니다.
| 방법 | 경로 | 지원 형식 |
|---|---|---|
| 웹 UI | OpenWebUI(:3000) → 문서/지식 업로드 | PDF·TXT·CSV·DOCX |
| API | POST localhost:8000/documents/upload | PDF·TXT·CSV·DOCX |
| 목록 확인 | GET localhost:8000/documents/list | — |
API로 문서 업로드
curl -X POST http://localhost:8000/documents/upload \
-H "X-Internal-Secret: $INTERNAL_SECRET" \
-F "file=@./manual.pdf"
2. 통화 녹음 · PDF 보고서 (전화봇)
Twilio 전화봇은 통화 녹음(.mp3)과 PDF 보고서를 컨테이너 안 /app/data/recordings·/app/data/reports에 저장합니다. 목록 조회와 다운로드 API가 있으며, 녹음·보고서 기능은 실시간 토글로 켜고 끌 수 있습니다.
| 동작 | 경로 | 비고 |
|---|---|---|
| 녹음 목록 | GET localhost:5000/recordings | API 시크릿 필요 |
| 녹음 다운로드 | GET /recordings/<파일명>.mp3 | 파일명 검증 |
| 보고서 목록 | GET localhost:5000/reports | API 시크릿 필요 |
| 보고서 다운로드 | GET /reports/<파일명>.pdf | 파일명 검증 |
| 기능 켜고 끄기 | POST /toggle/recording · /toggle/pdf-report | 실시간 전환 |
3. Telegram으로 올리기 (파일 · 음성)
업로드는 20MB로 제한되며, 확장자 위조를 막기 위해 Magic Bytes(파일 시그니처) 검증을 거칩니다.
4. 브라우저 에이전트 결과물
브라우저 에이전트는 작업 산출물을 ~/OpenWebUI/browser-agent/data 아래에 종류별로 쌓습니다. 스크린샷은 POST /screenshot으로 즉석 생성할 수 있습니다.
| 종류 | 저장 위치 |
|---|---|
| 스크린샷 | data/screenshots |
| 세션 저장본 | data/sessions |
| 작업 결과 | data/results |
| 감사 로그 | data/audit |
5. 미디어 업로드 페이지 (사진·동영상·음성·PDF)
드래그앤드롭 업로드 화면(localhost:8000/upload)으로 사진·동영상·음성·PDF를 올릴 수 있습니다. 저장 위치는 컨테이너의 /app/media이며, 이는 호스트의 공유 폴더(예: ~/ai-share)에 마운트됩니다.
컨테이너는 보안상 비root 사용자(apiuser, uid 1002)로 동작합니다. 호스트의 공유 폴더 소유자가 다르면 업로드 시
500 (Permission denied) 오류가 납니다. 첫 사용 전에 폴더 소유권을 컨테이너 사용자에 맞춰 주세요.
공유 폴더 쓰기 권한 부여 (호스트에서)
# 마운트된 공유 폴더 소유권을 컨테이너 사용자(1002)로 변경
sudo chown 1002:1002 /home/<사용자>/ai-share
# 하위 폴더(예: photos/홍길동)에도 저장한다면
# 단, 읽기전용 마운트(recordings·reports)는 제외
sudo find /home/<사용자>/ai-share -mindepth 1 -maxdepth 1 \
! -name recordings ! -name reports -exec chown -R 1002:1002 {} +
# 적용 확인 — 소유자가 1002로 보이면 정상
docker exec openwebui-openapi-tools-1 ls -ld /app/media
실제 마운트 경로 확인
공유 폴더의 실제 호스트 경로는 아래로 확인할 수 있습니다. 결과의 /app/media 대상에 연결된 Source 값이 권한을 줄 폴더입니다.
docker inspect openwebui-openapi-tools-1 --format '{{json .Mounts}}'
📋 업로드 공통 보안
413으로 거부되고, 빈 파일은 색인되지 않습니다.
✅설치 검증 (verify-install.sh)
전체 시스템(Phase 2 + Phase 3 + Browser Agent)을 한 번에 검증하는 스크립트입니다.
실행 방법
chmod +x verify-install.sh
./verify-install.sh
검증 항목 (13개 섹션)
| # | 검증 항목 | 내용 |
|---|---|---|
| 1 | 디렉토리 구조 (25개) | Phase2/Phase3/Browser 폴더 존재 + 권한 |
| 2 | 필수 파일 (38개) | .env, docker-compose, 소스코드 등 |
| 3 | 보안 권한 | .env 600, secrets 700, API 키 설정 |
| 4 | Docker 컨테이너 | 6개 컨테이너 실행 상태 + 포트 |
| 5 | API 헬스 | 각 서비스 /health 엔드포인트 응답 |
| 6 | Docker 네트워크 | 컨테이너 간 통신 연결 확인 |
| 7 | 보안 점검 | 포트 바인딩, UFW, Nginx 설정 |
| 8 | Browser Use + Chromium | Browser Use, Chromium, 멀티프로바이더 패키지 |
| 9 | seccomp 프로파일 | JSON 유효성, syscall 수 |
| 10 | Telegram 설정 | BOT TOKEN, 관리자 ID |
| 11 | Twilio + Telegram 연동 | Account SID, 알림 연동 |
| 12 | OpenWebUI Tool 등록 | 브라우저 에이전트, 전화, SMS 등 도구 |
| 13 | Cloudflare Tunnel | 선택 항목 |
검증 결과 해석
✅ 정상 │ ⚠️ 경고 (동작에 영향 없음) │ ❌ 실패 (수정 필요) │ ℹ️ 정보
🔧유지보수
🎛️ ai_config.py — AI 동작 설정 (전화봇)
ai_config.py 파일 하나로 조절합니다. 코드를 건드릴 필요 없이 이 파일만 수정하면 됩니다.
# 1) 파일 편집
nano ~/OpenWebUI/twilio-bot/ai_config.py
# 2) 적용 (재시작)
cd ~/OpenWebUI && docker compose restart twilio-bot
① 끼어들기(barge-in)
| 설정 | 기본값 | 의미 / 조절 |
|---|---|---|
BARGEIN_THRESHOLD | 0.6 | 민감도. 0.8=잘 끼어듦 / 0.4=둔감(소음 환경 권장) |
BARGEIN_MIN_SECONDS | 3.0 | 이보다 짧은 답변은 끼어들기 무시 |
BARGEIN_ENABLED | True | False면 끼어들기 처리 완전히 끔 |
BARGEIN_NOTE | (지시문) | 끼어들 때 AI 반응 스타일 |
| 🆕 음성 대기시간 — 봇이 성급하게 끊을 때 키우세요 | ||
SPEECH_TIMEOUT_ADMIN | "5" | 관리자 통화(일정 등록 등 긴 명령). 넉넉하게 권장 |
SPEECH_TIMEOUT_INBOUND | "3" | 걸려온 전화 응대 |
SPEECH_TIMEOUT_OUTBOUND | "3" | 봇이 거는 안부전화 |
BARGEIN_THRESHOLD = 0.4로 낮추거나 BARGEIN_ENABLED = False로 끄세요.SPEECH_TIMEOUT_* 값을 키우세요(초 단위 문자열, 예 "7"). 값이 클수록 말을 멈춰도 안 끊기지만, 너무 크면 말을 끝낸 뒤에도 그만큼 기다려 답답할 수 있습니다.② 상담원(사람) 연결 — 0번 / 음성
| 설정 | 기본값 | 의미 |
|---|---|---|
OPERATOR_TRANSFER_ENABLED | True | 키패드 0번 연결 |
OPERATOR_VOICE_ENABLED | True | 음성 "담당자" 연결 |
OPERATOR_HINT_ENABLED | True | "0번 눌러주세요" 안내 멘트 |
OPERATOR_VOICE_KEYWORDS | (목록) | 음성 연결 키워드 (추가/삭제 가능) |
# 완전 무인 AI 응대 (사람 연결 모두 끔)
OPERATOR_TRANSFER_ENABLED = False
OPERATOR_VOICE_ENABLED = False
# 음성 키워드 늘리기
OPERATOR_VOICE_KEYWORDS = ["담당자", "상담원", "직원", "사람"]
③ AI 성격 / 언어 / 타이밍
| 설정 | 의미 |
|---|---|
DEFAULT_LANG | 기본 언어 ("ko"/"en"/"ja"/"zh") |
ADMIN_SYSTEM_PROMPTS | 관리자 전화 시 AI 성격 (4개국어) |
INBOUND_SYSTEM_PROMPTS | 외부 전화 시 AI 성격 (4개국어) |
MESSAGES | 인사·안내 등 고정 멘트 (4개국어) |
TIMEOUT_INBOUND / OUTBOUND | 상대방 말 시작 대기 시간 |
INBOUND_SYSTEM_PROMPTS의 "ko" 항목을 수정하면 외부 고객에게 응대하는 AI의 정체성·말투를 바꿀 수 있습니다 (예: "○○회사 상담원입니다").OpenWebUI 버전 다운그레이드 (예: v0.9.5 → v0.9.2)
# 1. OpenWebUI 디렉토리로 이동
cd ~/OpenWebUI
# 2. docker-compose.yml의 이미지 태그를 원하는 버전으로 변경
# (예: :main → :v0.9.2 / 다른 버전도 동일 방식)
sed -i 's|ghcr.io/open-webui/open-webui:main|ghcr.io/open-webui/open-webui:v0.9.2|g' docker-compose.yml
# 3. 컨테이너 내리고 새 버전으로 재시작
docker compose down && docker compose up -d
# 4. 브라우저에서 Ctrl+Shift+R 로 캐시 초기화
# ──────────────────────────────────────
# 📌 다시 최신 버전으로 복원하려면:
cd ~/OpenWebUI
sed -i 's|ghcr.io/open-webui/open-webui:v0.9.2|ghcr.io/open-webui/open-webui:main|g' docker-compose.yml
docker compose down && docker compose up -d
버전 변경 시 이전 Docker 이미지가 디스크에 남습니다. 아래 명령어로 정리하세요.
# 사용하지 않는 이미지 확인
docker system df
# 미사용 이미지 모두 삭제 (현재 실행 중인 컨테이너는 영향 없음)
docker image prune -a -f
⚠️ DB 스키마 변경이 포함된 메이저 업데이트(예: v0.9.0) 이전 버전으로 되돌릴 경우 호환성 문제가 발생할 수 있으므로, 큰 폭의 다운그레이드 전에는 반드시 백업하세요.
💾 백업 & 복원
# ── 전체 백업 (업데이트 전 권장) ──
BACKUP_DIR=~/backup_$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
# 환경변수 + 시크릿
cp ~/OpenWebUI/.env $BACKUP_DIR/
cp -r ~/OpenWebUI/secrets/ $BACKUP_DIR/
# 연락처 + 통화기록 + 녹음 + PDF
cp -r ~/OpenWebUI/twilio-bot/data/ $BACKUP_DIR/twilio-data/
# RAG 문서 원본
cp -r ~/OpenWebUI/openapi-tools/data/ $BACKUP_DIR/rag-data/
# Docker Compose 설정
cp ~/OpenWebUI/docker-compose.yml $BACKUP_DIR/
echo "✅ 백업 완료: $BACKUP_DIR"
ls -la $BACKUP_DIR/
# ── 백업에서 복원 ──
BACKUP_DIR=~/backup_20260519_120000 # 백업 폴더명 확인 후 입력
# 환경변수 + 시크릿 복원
cp $BACKUP_DIR/.env ~/OpenWebUI/.env
cp -r $BACKUP_DIR/secrets/ ~/OpenWebUI/
# 데이터 복원
cp -r $BACKUP_DIR/twilio-data/ ~/OpenWebUI/twilio-bot/data/
cp -r $BACKUP_DIR/rag-data/ ~/OpenWebUI/openapi-tools/data/
# 재시작
cd ~/OpenWebUI && docker compose restart
echo "✅ 복원 완료"
백업 불필요: Docker 이미지(pull로 재다운로드), Qdrant 벡터(연락처 재로딩 시 재생성), 로그 파일
🔒 수동 보안 업데이트
# ── ① OS 보안 패치 ──
sudo apt update && sudo apt upgrade -y
# ── ② Docker 이미지 최신 버전 갱신 ──
cd ~/OpenWebUI && docker compose pull && docker compose up -d
# ── ③ 브라우저 에이전트 갱신 ──
cd ~/OpenWebUI && docker compose pull browser-agent && docker compose up -d browser-agent
# ── ④ Telegram 브릿지 갱신 ──
cd ~/telegram-openwebui-bridge && docker compose pull && docker compose up -d
# ── ⑤ Ollama 업데이트 ──
curl -fsSL https://ollama.ai/install.sh | sh
# ── ⑥ 미사용 Docker 이미지 정리 (디스크 절약) ──
docker image prune -a -f
🔑 API 키 수동 교체 (보안 사고 시)
# ── API_SECRET 교체 (Twilio 봇 내부 인증) ──
NEW_API=$(openssl rand -hex 24)
sed -i "s/API_SECRET=.*/API_SECRET=$NEW_API/" ~/OpenWebUI/.env
cd ~/OpenWebUI && docker compose restart twilio-bot openapi-tools
echo "새 API_SECRET: $NEW_API"
# ── 관리자 번호(ADMIN_NUMBERS) 변경 — PIN은 폐지됨 ──
# 봇에게 전화 걸 수 있는 번호 목록 (쉼표로 여러 개 가능)
read -p "관리자 번호 (예: +821012345678,+821099998888): " NEW_ADMINS
sed -i "s/ADMIN_NUMBERS=.*/ADMIN_NUMBERS=$NEW_ADMINS/" ~/OpenWebUI/.env
cd ~/OpenWebUI && docker compose up -d twilio-bot
# ── Groq API 키 교체 ──
read -p "새 Groq API Key: " NEW_GROQ
sed -i "s/OPENAI_API_KEY=.*/OPENAI_API_KEY=$NEW_GROQ/" ~/OpenWebUI/.env
echo "$NEW_GROQ" > ~/OpenWebUI/secrets/groq_api_key
cd ~/OpenWebUI && docker compose restart
📋 전체 시스템 상태 확인
# 전체 컨테이너 상태
docker ps --format "table {{.Names}}\t{{.Status}}"
# 디스크 사용량
docker system df
# 각 서비스 헬스체크
curl -s http://localhost:3000/health # OpenWebUI
curl -s http://localhost:8001/health # 브라우저 에이전트
curl -s http://localhost:8444/health | jq . # Telegram 브릿지
docker exec twilio-bot curl -s http://127.0.0.1:5000/health # Twilio 봇
# 최근 에러 로그
docker logs twilio-bot --tail 20 2>&1 | grep -i "error\|fail"
docker logs browser-agent --tail 20 2>&1 | grep -i "error\|fail"
🆕 채팅 캘린더 "Read timed out (15초)" 해결 — 멀티 워커
HTTPConnectionPool... Read timed out (read timeout=15) 오류를 내는데, 터미널에서 curl로 같은 API를 부르면 즉시(0.02초) 응답하는 경우 — 이것은 단일 워커 self-call 데드락입니다.
OpenWebUI가 워커 1개로 도구를 실행하는데, 그 도구가 다시 OpenWebUI 자기 API를 호출하니 응답할 워커가 없어 15초 동안 막히는 것입니다. 현재 스크립트는 이를 자동 해결(워커 4개)하지만, 기존 설치를 수동 수정하려면:
cd ~/OpenWebUI
# docker-compose.yml의 open-webui environment에 추가:
# - UVICORN_WORKERS=4
# 재생성 ("Started/Recreated"가 떠야 함, "Running"만 뜨면 미반영)
docker compose up -d open-webui
# 확인
docker exec openwebui-open-webui-1 sh -c 'cat /proc/1/environ | tr "\0" "\n" | grep UVICORN_WORKERS'
.env에 UVICORN_WORKERS=2를 넣어 줄일 수 있습니다 (2개로도 데드락 해결).🔒보안 체크리스트 (91항목)
🆕 최신 보안 강화 (이번 업데이트)
| 항목 | 내용 |
|---|---|
| 통화 인증 | PIN 폐지 → 등록된 관리자 번호만 (유출 위험 제거) |
| requests 패치 | requests>=2.34.2 — CVE-2024-47081 (netrc 자격증명 유출) 차단 |
| urllib3 패치 | urllib3>=2.6.3 — CVE-2026-21441 (DoS) 차단 |
| trust_env=False | 캘린더 조회 시 환경 자격증명 비활성화 + 리다이렉트 차단 |
| 캘린더 권한 | 고객 상담 모드에서도 캘린더는 관리자 전용으로 분리 |
| 재발 방지 | 캘린더 마운트를 메인 compose에 고정 → 어떤 방식으로 띄워도 유지 |
| 가짜 일정 방지 | 조회 실패 시 AI가 지어내지 않고 정확한 원인 안내 |
🔒전체 보안 체크리스트 (91항목)
Phase 2 인프라 44개 + Browser Agent 21개 + Telegram 봇 브릿지 26개 = 총 91개 보안 항목.
📞 Twilio 전화봇 (23개)
| # | 항목 | 설명 |
|---|---|---|
| 1 | API 키 인증 | OPENAI_API_KEY / TWILIO_AUTH_TOKEN 환경변수 분리 |
| 2 | Webhook 서명 검증 | X-Twilio-Signature HMAC-SHA1 검증 — 위조 요청 차단 |
| 3 | 발신번호 화이트리스트 | 허용된 번호 목록 외 수신 전화 자동 차단 |
| 4 | 차단번호 블랙리스트 | blocked_numbers.json — 재시작 후에도 영구 유지 |
| 5 | TwiML 응답 검증 | 비정상 TwiML 응답 차단 + 오류 자동 처리 |
| 6 | 통화 시간 제한 | MAX_CALL_DURATION (기본 5분) — 무한 통화 방지 |
| 7 | 녹음 암호화 저장 | AES-256 녹음 파일 암호화 (선택) |
| 8 | 통화 인증 | 등록된 관리자 번호만 통화 허용 (PIN 폐지 — 번호 기반이 더 안전) |
| 9 | Rate Limiting | 분당 10회 발신/수신 제한 + IP별 제한 |
| 10 | 입력 길이 제한 | 음성→텍스트 변환 결과 512자 제한 |
| 11 | Prompt Injection 방어 | 통화 내용에서 시스템 명령 패턴 필터링 |
| 12 | 민감정보 마스킹 | 로그에서 API키·계좌번호·전화번호 자동 마스킹 |
| 13 | non-root 컨테이너 | UID 1001 비권한 사용자로 실행 |
| 14 | read_only 파일시스템 | 루트 파일시스템 읽기 전용 마운트 |
| 15 | cap_drop ALL | 모든 Linux Capabilities 제거 |
| 16 | 네트워크 격리 | Docker 내부 네트워크 — 포트 127.0.0.1 바인딩 |
| 17 | Docker Secrets | 민감 정보 파일 분리 저장 (:ro 마운트) |
| 18 | .env chmod 600 | 소유자만 읽기 — 자동 백업 생성 |
| 19 | 통화 세션 타임아웃 | 비활동 3분 후 자동 세션 종료 |
| 20 | RAG 입력 검증 | 문서 검색 쿼리 길이·내용 검증 |
| 21 | PDF 업로드 제한 | 10MB 상한 · 확장자 검증 · Path Traversal 방어 |
| 22 | Healthcheck 자동복구 | 30초 간격 · 재시작 감지 시 자동 복구 |
| 23 | Telegram 알림 CHAT_ID | 지정 채팅방에만 알림 전송 — 무단 수신 차단 |
🌐 Nginx (6개)
| # | 항목 | 설명 |
|---|---|---|
| 24 | HTTPS 강제 | HTTP→HTTPS 301 리다이렉트 |
| 25 | HSTS 헤더 | max-age=31536000; includeSubDomains |
| 26 | Rate Limiting | limit_req_zone — IP당 분당 60회 제한 |
| 27 | 보안 헤더 | X-Frame-Options · X-Content-Type · CSP · Referrer-Policy |
| 28 | JSON 감사 로그 | 요청/응답 JSON 포맷 감사 로그 기록 |
| 29 | Webhook 경로만 노출 | /telegram-webhook만 프록시 — 나머지 차단 |
🐳 Docker (3개)
| # | 항목 | 설명 |
|---|---|---|
| 30 | no-new-privileges | 컨테이너 내 권한 상승 차단 |
| 31 | tmpfs /tmp | 임시 파일 메모리 저장 — 재시작 시 자동 삭제 |
| 32 | 메모리/CPU 제한 | deploy.resources.limits 설정 — OOM/DoS 방어 |
📝 로그/감사 (2개)
| # | 항목 | 설명 |
|---|---|---|
| 33 | 로그 로테이션 | 10MB × 3파일 자동 순환 — 디스크 고갈 방지 |
| 34 | 감사 로그 분리 | /var/log/nginx/audit.json.log 별도 저장 |
🤖 OpenWebUI (5개)
| # | 항목 | 설명 |
|---|---|---|
| 35 | 관리자 계정 설정 | 최초 실행 시 관리자 이메일/비밀번호 설정 |
| 36 | API Key 인증 | Bearer 토큰 기반 API 접근 제어 |
| 37 | 포트 로컬 바인딩 | 127.0.0.1:3000 — 외부 직접 접근 차단 |
| 38 | Qdrant 내부 전용 | Qdrant 포트 외부 미노출 |
| 39 | 볼륨 권한 관리 | open-webui-data 볼륨 소유권 관리 |
⚙️ Tools-API (4개)
| # | 항목 | 설명 |
|---|---|---|
| 40 | API Key 인증 | TOOLS_API_KEY 환경변수 기반 인증 |
| 41 | 포트 로컬 바인딩 | 127.0.0.1:8000 — 외부 접근 차단 |
| 42 | 입력값 검증 | Tool 파라미터 타입·길이 검증 |
| 43 | 응답 필터링 | 민감정보 제거 후 반환 |
🌐 Browser Agent v7 (21개)
기존 14개
| # | 항목 | 설명 |
|---|---|---|
| 44 | API Key 인증 (hmac) | BROWSER_AGENT_API_KEY hmac.compare_digest 검증 |
| 45 | SlowAPI Rate Limit | 엔드포인트별 분당 요청 횟수 제한 |
| 46 | CORS 제한 | 허용 Origin 화이트리스트 |
| 47 | TrustedHost 미들웨어 | 신뢰된 호스트 헤더만 허용 |
| 48 | 보안 헤더 6종 | X-Frame-Options · X-Content-Type · CSP 등 |
| 49 | 요청 본문 10KB 제한 | 과도한 요청 본문 차단 |
| 50 | URL 차단 패턴 | 내부망·localhost·위험 도메인 접근 차단 |
| 51 | Prompt Injection 감지 | 브라우저 작업 태스크에서 명령 주입 패턴 차단 |
| 52 | 타임아웃 3종 | task(180s) · multi(300s) · step(30s) 제한 |
| 53 | 동시 실행 제한 | MAX_CONCURRENT=3 — 동시 브라우저 3개 이하 |
| 54 | Seccomp 프로파일 | 허용 syscall 화이트리스트 (seccomp-browser.json) |
| 55 | cap_drop + read_only | 모든 Capabilities 제거 · 루트 파일시스템 읽기 전용 |
| 56 | non-root (UID 1001) | 비권한 appuser 계정으로 실행 |
| 57 | 감사 로그 (audit.log) | 모든 API 호출 감사 로그 기록 |
신규 7개
| # | 항목 | 설명 |
|---|---|---|
| 58 | 🆕 IP 차단 블랙리스트 | 5회 인증 실패 → IP 30분 잠금 · ip_blacklist.json 영구 저장 |
| 59 | 🆕 요청 서명 (HMAC-SHA256) | X-Timestamp + X-Signature로 요청 위조·Replay 방지 (선택 활성화) |
| 60 | 🆕 AI 응답 민감정보 필터링 | API Key·JWT·전화번호를 응답 전 자동 마스킹 |
| 61 | 🆕 Path Traversal 이중 검증 | null byte + realpath()로 심볼릭 링크 우회까지 차단 |
| 62 | 🆕 메모리 입력값 스키마 검증 | 허용 키 화이트리스트 + 값 타입·길이 제한 |
| 63 | 🆕 Docker 리소스 제한 강화 | pids:100 + ulimits nofile(1024/2048) + nproc(128/256) |
| 64 | 🆕 감사 로그 JSON 구조화 | 이벤트별 JSON 필드 + RotatingFileHandler(10MB×3) |
💬 Telegram 봇 브릿지 (26개)
기존 18개
| # | 항목 | 설명 |
|---|---|---|
| 65 | 화이트리스트 접근 제어 | 허용된 Telegram User ID만 접근 — 비관리자 전면 차단 |
| 66 | Rate Limiting | 분당 30회 제한 + 3회 실패 시 점진적 차단 (10→30→60→120분) |
| 67 | Webhook 서명 검증 | Telegram Secret Token으로 요청 위조 방지 |
| 68 | 통화 인증 (번호 기반) | 등록된 관리자 번호만 통화 허용 · PIN 방식 폐지로 유출 위험 제거 |
| 69 | 입력 길이 제한 | 4096자 제한 + null byte 제거 + XSS 방어 |
| 70 | 민감정보 로그 마스킹 | API Key · Bearer 토큰 · 전화번호 자동 마스킹 |
| 71 | non-root 컨테이너 | UID 1001 botuser · /sbin/nologin 셸 |
| 72 | cap_drop + read_only | 모든 Linux 권한 제거 · 루트 파일시스템 읽기 전용 |
| 73 | 파일 업로드 제한 | 20MB 상한 · Path Traversal 방어 · 파일명 정규화 |
| 74 | 세션 타임아웃 | 30분 비활동 시 세션 초기화 · 세션 수 100개 상한 |
| 75 | Docker Secrets | Bot Token · API Key · Webhook Secret 파일 분리 저장 |
| 76 | .env chmod 600 | 소유자만 읽기/쓰기 · 자동 백업 생성 |
| 77 | Model/Tool ID 검증 | 정규식 화이트리스트 패턴 + 128자 길이 제한 |
| 78 | 네트워크 격리 | Docker 내부 네트워크 · 포트 127.0.0.1 바인딩 |
| 79 | 로그 로테이션 | 10MB × 3파일 자동 순환 |
| 80 | Health Check 자동 복구 | 30초 간격 · Restarting 감지 시 권한 자동 복구 |
| 81 | 허용 사용자 영구 저장 | /app/data/allowed_users.json · chmod 600 |
| 82 | MAX_SESSIONS DoS 방어 | 세션 100개 초과 시 가장 오래된 세션 자동 제거 |
신규 8개
| # | 항목 | 설명 |
|---|---|---|
| 83 | 🆕 Replay Attack 방어 | update_id를 deque(10000건)에 캐싱 — 중복 요청 즉시 무시 |
| 84 | 🆕 Prompt Injection 방어 | 9개 패턴 정규식 감지 ("ignore previous", jailbreak, system 태그 등) |
| 85 | 🆕 파일 Magic Bytes 검증 | 업로드 파일 실제 바이트 확인 — 확장자 위조 방지 (8종) |
| 86 | 🆕 AI 응답 민감정보 필터링 | API Key·JWT·전화번호를 응답 전송 전 자동 마스킹 |
| 87 | 🆕 구조화된 감사 로그 | /app/logs/audit.log JSON 형식 (timestamp·event·user_id·ok·detail) |
| 88 | 🆕 비상 차단 모드 | /emergency 명령으로 즉시 토글 — 관리자 외 모든 세션 강제 종료 |
| 89 | 🆕 대시보드 Brute-force 방지 | 토큰 5회 실패 시 해당 IP 15분 잠금 · timing-safe 비교 |
| 90 | 🆕 Seccomp 프로파일 | Python 봇 허용 syscall 화이트리스트 (seccomp-bot.json) |
| 91 | 🆕 WSL2 Seccomp 자동 대체 | WSL2 환경 자동 감지 → privileged 모드로 openat2 오류 우회 (재설치 시 자동 적용) |
privileged: true 모드로 대체됩니다. PowerShell(관리자)에서 wsl --update 실행 후 재설치하면 Seccomp 정상 적용됩니다.
📂디렉토리 구조
~/
├── OpenWebUI/
│ ├── .env # 환경변수 (chmod 600)
│ ├── docker-compose.yml
│ ├── browser-agent/ # AI 브라우저 에이전트 v7
│ │ ├── agent_server.py # FastAPI 서버 (29KB, WrappedLLM)
│ │ ├── openwebui_tool.py # OpenWebUI 도구 (16KB, Valves 12개)
│ │ ├── multi_agent/ # Multi-Agent (4개 AI 협업)
│ │ ├── data/ # 메모리(user_memory.json), 감사로그
│ │ └── secrets/ # API 키 (:ro 마운트)
│ ├── tools-api/ # Phase 2 스텁
│ └── twilio-bot/ # Phase 2 스텁
│
├── telegram-openwebui-bridge/ # Telegram 봇 (v2.0.0)
│ ├── bot/
│ │ ├── telegram_bot.py # 봇 본체 (스트리밍·예약·보안 26항목)
│ │ ├── seccomp-bot.json # Seccomp 프로파일
│ │ └── Dockerfile
│ ├── data/
│ │ ├── schedules.json # 예약 목록 (재시작 후 유지)
│ │ ├── allowed_users.json # 허용 사용자 목록
│ │ └── trusted_users.json # (미사용 — PIN 폐지)
│ ├── logs/
│ │ ├── bot.log # 일반 로그 (10MB×3 로테이션)
│ │ └── audit.log # 감사 로그 (JSON 구조화)
│ ├── secrets/ # API Key · Webhook Secret (:ro)
│ ├── .env # 환경변수 (chmod 600)
│ └── docker-compose.yml
│
├── ai-share/ # 로컬 파일 공유 폴더 (read_file/save_file)
│
├── setup-browser-agent-calendar.sh
├── start-openwebui-hardened-admin-only.sh
├── setup-telegram-bridge-calendar.sh
└── verify-install.sh # 설치 검증 스크립트