⑤ 운영

OpenWebUI + Twilio AI 전화비서 + AI 브라우저 에이전트 + Telegram 통합 설치 가이드

Ubuntu → OpenWebUI + Browser Agent v7 + Telegram 원클릭 설치 | 보안 91항목 · 스트리밍 · 예약

🤖 OpenWebUI 💬 Telegram 🌐 Browser Agent v7 🔒 보안 91항목
Phase2 44 + Browser Agent 21 + Telegram 봇 26
DASHBOARDS

📊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/dashboardRAG 벡터 DB 저장 현황✅ 로컬에서 직접 접속
6📖 RAG API 문서:8000/docsOpenAPI 엔드포인트 목록·테스트✅ 로컬에서 직접 접속
전화비서(:5000)와 Telegram 봇(:8445)은 127.0.0.1에 바인딩되어 외부에서 직접 접근 불가합니다. SSH 터널 또는 포트포워딩으로 접속합니다. 나머지(:3000·:8001·:6333·:8000)는 로컬/서버에서 바로 열 수 있습니다.

1️⃣ OpenWebUI 메인 (:3000)

시스템의 중심이 되는 웹 UI입니다. 채팅, Tool 관리(Workspace → Tools), 모델 설정, 사용자/관리자 계정 관리, API Key 발급을 모두 여기서 합니다.

접속: 브라우저에서 http://서버IP:3000
로그인: 설치 시 입력한 관리자 이메일/비밀번호

API Key 발급 (Telegram 브릿지 설치에 필요):
  1. 좌측 하단 사용자 아이콘설정
  2. 계정 탭 → API Keys 섹션
  3. 새 API Key 생성 → 키 복사 후 안전한 곳에 저장
Tool 활성화는 Workspace → Tools에서 합니다. 전화비서·브라우저 에이전트·미디어 관리 등 각 기능은 해당 Tool이 켜져 있어야 채팅에서 호출됩니다.

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
WSL(Windows) 환경에서만 작동합니다. 녹음·보고서 폴더도 바로 열 수 있습니다:
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초 자동 새로고침 토글선택
📢 공지모든 활성 사용자에게 메시지 전송

🔑 인증 토큰 확인

대시보드는 토큰 기반 인증을 사용합니다. 잘못된 토큰을 5회 입력하면 해당 IP가 15분 잠금됩니다.

# 서버에서 토큰 확인
grep INTERNAL_API_SECRET ~/telegram-openwebui-bridge/.env
한 번 입력 후 브라우저에 자동 저장됩니다. Telegram에서는 /admin → 웹 대시보드 버튼으로 토큰 앞부분을 확인할 수 있습니다.

🖥️ 접속 방법 1 — SSH 터널 (보안 권장)

1단계: PC/Mac 터미널에서 SSH 터널 연결
ssh -L 8445:localhost:8445 user@서버IP
2단계: Windows 브라우저에서 접속
http://localhost:8445/dashboard
SSH 터널은 암호화 채널이므로 Nginx HTTPS보다 보안이 강합니다.

🖥️ 접속 방법 2 — Windows 포트포워딩 (WSL2 전용)

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 포트 변경 (가장 간단)

docker-compose.yml의 포트 바인딩을 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 접속.

⚠️ 주의: 0.0.0.0 바인딩 시 같은 네트워크의 다른 기기에서도 접근 가능합니다. UFW에서 8445 포트를 차단하거나 사용 후 127.0.0.1로 되돌리세요.

📱 Telegram 명령어로 대신하기

대시보드의 대부분 기능은 Telegram 명령어로 대체 가능합니다.

웹 탭Telegram 명령어
📊 개요/admin → 현황  |  /stats  |  /status
👥 세션/users
👤 사용자/adduser  |  /block  |  /unblock
🔧 Tool/tools
📋 로그/logs  |  /logs 50
📢 공지/broadcast 메시지

4️⃣ Browser Agent API 문서 (:8001/docs)

브라우저 에이전트는 REST API로 직접 호출할 수 있고, FastAPI 자동 문서(Swagger UI)를 제공합니다.

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)

RAG(검색 증강 생성)에 사용되는 벡터 데이터베이스의 웹 콘솔입니다. 저장된 컬렉션과 벡터 현황을 확인합니다.

대시보드 접속: http://localhost:6333/dashboard (로컬에서 직접)
컬렉션 조회 (터미널):
# 저장된 컬렉션 목록 확인
curl -s http://localhost:6333/collections
임베딩은 Ollama의 nomic-embed-text 모델로 생성되어 Qdrant에 저장됩니다. RAG 사용법 상세는 아래 RAG 섹션을 참고하세요.

6️⃣ RAG / Tools API 문서 (:8000/docs)

OpenWebUI에 연결되는 Tools/RAG 서버의 OpenAPI 문서입니다. 등록된 엔드포인트를 목록으로 보고 브라우저에서 바로 테스트할 수 있습니다.

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
recordings · reports 폴더는 제외하세요. 통화 녹음(~/OpenWebUI/twilio-bot/data/recordings/)과 PDF 보고서(~/OpenWebUI/twilio-bot/data/reports/)는 읽기전용(:ro)으로 마운트되므로 소유권을 바꾸면 안 됩니다. ②번 명령은 이 둘을 자동으로 건너뜁니다.
RAG

📚RAG 문서 검색

PDF 문서를 업로드하면 AI가 내용을 벡터로 변환하여 Qdrant에 저장합니다. 이후 채팅이나 전화에서 질문하면 문서에서 관련 내용을 찾아 답변합니다.

동작 원리

① PDF 업로드 → 텍스트 추출 → Ollama(nomic-embed-text)로 벡터 변환 → Qdrant에 저장
② 질문 → 질문을 벡터 변환 → Qdrant에서 유사 문서 검색 → AI가 참고하여 답변 생성

📄 PDF 업로드 방법

방법 1: 채팅창에서 업로드 (권장)

OpenWebUI 또는 Telegram 채팅창에서 RAG 문서 검색 Tool을 활성화한 후:

👤 이 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가 자동으로 RAG를 검색합니다.

예시: 고객이 전화해서 "환불 정책이 어떻게 되나요?" → AI가 PDF에서 환불 정책을 찾아 음성으로 답변

※ 안부전화(발신)에서는 속도 최적화를 위해 RAG 검색이 비활성화되어 있습니다. 수신전화에서만 작동합니다.

📁 저장 위치

항목경로
업로드된 PDF 원본~/OpenWebUI/openapi-tools/data/
벡터 데이터 (Qdrant)~/OpenWebUI/qdrant/
임베딩 모델Ollama nomic-embed-text
MEDIA

🗂️미디어 관리 · 업로드

문서(RAG), 통화 녹음·보고서, 브라우저 결과물 등 종류별로 업로드 경로와 저장 위치가 다릅니다. 업로드는 모두 파일명 정제·크기 제한·형식 검증을 거칩니다.

1. RAG 문서 업로드 (PDF·TXT·CSV·DOCX)

지식 기반 답변에 쓰일 문서는 두 가지 방법으로 올립니다. 일반 사용은 OpenWebUI 화면에서 끌어다 놓으면 되고, 자동화·API 연동 시에는 OpenAPI 도구 서버의 업로드 엔드포인트를 직접 호출합니다. 업로드하면 텍스트 추출 → 스마트 청킹 → Qdrant 벡터 색인까지 자동 진행되며, 같은 파일은 기존 벡터를 지우고 다시 색인합니다.

방법경로지원 형식
웹 UIOpenWebUI(:3000) → 문서/지식 업로드PDF·TXT·CSV·DOCX
APIPOST localhost:8000/documents/uploadPDF·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/recordingsAPI 시크릿 필요
녹음 다운로드GET /recordings/<파일명>.mp3파일명 검증
보고서 목록GET localhost:5000/reportsAPI 시크릿 필요
보고서 다운로드GET /reports/<파일명>.pdf파일명 검증
기능 켜고 끄기POST /toggle/recording · /toggle/pdf-report실시간 전환

3. Telegram으로 올리기 (파일 · 음성)

Telegram 채팅에서 직접 미디어를 보낼 수 있습니다. PDF·이미지를 보내면 OpenWebUI에 자동 업로드되어 RAG로 색인되고, 음성 메시지를 보내면 Whisper STT로 텍스트 변환 → AI 응답 → TTS 음성 회신까지 이어집니다.

업로드는 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}}'

📋 업로드 공통 보안

모든 업로드는 위험 문자를 제거한 안전 파일명으로 저장되고 Path Traversal(상위 경로 탈출)을 이중 차단합니다. RAG 업로드는 최대 용량 초과 시 413으로 거부되고, 빈 파일은 색인되지 않습니다.
VERIFY

설치 검증 (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 키 설정
4Docker 컨테이너6개 컨테이너 실행 상태 + 포트
5API 헬스각 서비스 /health 엔드포인트 응답
6Docker 네트워크컨테이너 간 통신 연결 확인
7보안 점검포트 바인딩, UFW, Nginx 설정
8Browser Use + ChromiumBrowser Use, Chromium, 멀티프로바이더 패키지
9seccomp 프로파일JSON 유효성, syscall 수
10Telegram 설정BOT TOKEN, 관리자 ID
11Twilio + Telegram 연동Account SID, 알림 연동
12OpenWebUI Tool 등록브라우저 에이전트, 전화, SMS 등 도구
13Cloudflare Tunnel선택 항목

검증 결과 해석

정상  │  ⚠️ 경고 (동작에 영향 없음)  │  실패 (수정 필요)  │  ℹ️ 정보
MAINTENANCE

🔧유지보수

🎛️ ai_config.py — AI 동작 설정 (전화봇)

전화봇의 AI 성격, 끼어들기, 사람 연결, 타이밍 등을 ai_config.py 파일 하나로 조절합니다. 코드를 건드릴 필요 없이 이 파일만 수정하면 됩니다.
# 1) 파일 편집
nano ~/OpenWebUI/twilio-bot/ai_config.py

# 2) 적용 (재시작)
cd ~/OpenWebUI && docker compose restart twilio-bot
설정 파일이 없거나 깨져도 봇은 기본값으로 동작합니다(폴백 내장). 안심하고 수정하세요.

① 끼어들기(barge-in)

AI가 말하는 도중 상대방이 끼어들면 즉시 멈추고, 맞장구친 뒤 새 질문에 집중합니다.
설정기본값의미 / 조절
BARGEIN_THRESHOLD0.6민감도. 0.8=잘 끼어듦 / 0.4=둔감(소음 환경 권장)
BARGEIN_MIN_SECONDS3.0이보다 짧은 답변은 끼어들기 무시
BARGEIN_ENABLEDTrueFalse면 끼어들기 처리 완전히 끔
BARGEIN_NOTE(지시문)끼어들 때 AI 반응 스타일
🆕 음성 대기시간 — 봇이 성급하게 끊을 때 키우세요
SPEECH_TIMEOUT_ADMIN"5"관리자 통화(일정 등록 등 긴 명령). 넉넉하게 권장
SPEECH_TIMEOUT_INBOUND"3"걸려온 전화 응대
SPEECH_TIMEOUT_OUTBOUND"3"봇이 거는 안부전화
⚠️ 카페·길거리 등 소음이 큰 곳에서는 잡음에 AI가 멈출 수 있습니다. BARGEIN_THRESHOLD = 0.4로 낮추거나 BARGEIN_ENABLED = False로 끄세요.
🎤 봇이 말 도중에 끊는다면 SPEECH_TIMEOUT_* 값을 키우세요(초 단위 문자열, 예 "7"). 값이 클수록 말을 멈춰도 안 끊기지만, 너무 크면 말을 끝낸 뒤에도 그만큼 기다려 답답할 수 있습니다.

② 상담원(사람) 연결 — 0번 / 음성

외부인이 AI와 대화하다 사람(관리자)에게 연결되는 두 경로를 독립적으로 제어합니다.
설정기본값의미
OPERATOR_TRANSFER_ENABLEDTrue키패드 0번 연결
OPERATOR_VOICE_ENABLEDTrue음성 "담당자" 연결
OPERATOR_HINT_ENABLEDTrue"0번 눌러주세요" 안내 멘트
OPERATOR_VOICE_KEYWORDS(목록)음성 연결 키워드 (추가/삭제 가능)
# 완전 무인 AI 응대 (사람 연결 모두 끔)
OPERATOR_TRANSFER_ENABLED = False
OPERATOR_VOICE_ENABLED    = False

# 음성 키워드 늘리기
OPERATOR_VOICE_KEYWORDS = ["담당자", "상담원", "직원", "사람"]
모두 꺼도 외부인은 AI 상담을 계속 받고, 통화 후 관리자 자동 보고도 작동합니다. 캘린더 등 민감 기능은 항상 관리자 전용입니다.

③ 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)

tool calling 400 에러 등 특정 버전에서 문제가 발생할 때, 이전 안정 버전으로 되돌릴 수 있습니다.
# 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
사용 가능한 버전 태그는 GitHub Releases 페이지에서 확인할 수 있습니다.

⚠️ 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 "✅ 복원 완료"
백업 대상: .env(API 키), secrets/(시크릿), contacts.json(연락처), call_history.json(통화기록), recordings/(녹음 MP3), reports/(PDF 보고서), RAG 문서 원본
백업 불필요: Docker 이미지(pull로 재다운로드), Qdrant 벡터(연락처 재로딩 시 재생성), 로그 파일

🔒 수동 보안 업데이트

설치 후 보안 패치는 자동으로 적용되지 않습니다. 주기적으로(월 1회 권장) 아래 명령어를 실행하세요.
# ── ① 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 키가 유출되었거나 주기적으로 교체하고 싶을 때 사용합니다. 정상 작동 중이면 필요 없습니다.
# ── 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'
워커마다 메모리를 더 씁니다. 메모리가 빠듯하면 .envUVICORN_WORKERS=2를 넣어 줄일 수 있습니다 (2개로도 데드락 해결).
SECURITY

🔒보안 체크리스트 (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가 지어내지 않고 정확한 원인 안내
CVE 패치 버전은 작성 시점 기준입니다. 배포 전 최신 권고를 한 번 더 확인하세요.

🔒전체 보안 체크리스트 (91항목)

Phase 2 인프라 44개 + Browser Agent 21개 + Telegram 봇 브릿지 26개 = 총 91개 보안 항목.

📞 Twilio 전화봇 (23개)

#항목설명
1API 키 인증OPENAI_API_KEY / TWILIO_AUTH_TOKEN 환경변수 분리
2Webhook 서명 검증X-Twilio-Signature HMAC-SHA1 검증 — 위조 요청 차단
3발신번호 화이트리스트허용된 번호 목록 외 수신 전화 자동 차단
4차단번호 블랙리스트blocked_numbers.json — 재시작 후에도 영구 유지
5TwiML 응답 검증비정상 TwiML 응답 차단 + 오류 자동 처리
6통화 시간 제한MAX_CALL_DURATION (기본 5분) — 무한 통화 방지
7녹음 암호화 저장AES-256 녹음 파일 암호화 (선택)
8통화 인증등록된 관리자 번호만 통화 허용 (PIN 폐지 — 번호 기반이 더 안전)
9Rate Limiting분당 10회 발신/수신 제한 + IP별 제한
10입력 길이 제한음성→텍스트 변환 결과 512자 제한
11Prompt Injection 방어통화 내용에서 시스템 명령 패턴 필터링
12민감정보 마스킹로그에서 API키·계좌번호·전화번호 자동 마스킹
13non-root 컨테이너UID 1001 비권한 사용자로 실행
14read_only 파일시스템루트 파일시스템 읽기 전용 마운트
15cap_drop ALL모든 Linux Capabilities 제거
16네트워크 격리Docker 내부 네트워크 — 포트 127.0.0.1 바인딩
17Docker Secrets민감 정보 파일 분리 저장 (:ro 마운트)
18.env chmod 600소유자만 읽기 — 자동 백업 생성
19통화 세션 타임아웃비활동 3분 후 자동 세션 종료
20RAG 입력 검증문서 검색 쿼리 길이·내용 검증
21PDF 업로드 제한10MB 상한 · 확장자 검증 · Path Traversal 방어
22Healthcheck 자동복구30초 간격 · 재시작 감지 시 자동 복구
23Telegram 알림 CHAT_ID지정 채팅방에만 알림 전송 — 무단 수신 차단

🌐 Nginx (6개)

#항목설명
24HTTPS 강제HTTP→HTTPS 301 리다이렉트
25HSTS 헤더max-age=31536000; includeSubDomains
26Rate Limitinglimit_req_zone — IP당 분당 60회 제한
27보안 헤더X-Frame-Options · X-Content-Type · CSP · Referrer-Policy
28JSON 감사 로그요청/응답 JSON 포맷 감사 로그 기록
29Webhook 경로만 노출/telegram-webhook만 프록시 — 나머지 차단

🐳 Docker (3개)

#항목설명
30no-new-privileges컨테이너 내 권한 상승 차단
31tmpfs /tmp임시 파일 메모리 저장 — 재시작 시 자동 삭제
32메모리/CPU 제한deploy.resources.limits 설정 — OOM/DoS 방어

📝 로그/감사 (2개)

#항목설명
33로그 로테이션10MB × 3파일 자동 순환 — 디스크 고갈 방지
34감사 로그 분리/var/log/nginx/audit.json.log 별도 저장

🤖 OpenWebUI (5개)

#항목설명
35관리자 계정 설정최초 실행 시 관리자 이메일/비밀번호 설정
36API Key 인증Bearer 토큰 기반 API 접근 제어
37포트 로컬 바인딩127.0.0.1:3000 — 외부 직접 접근 차단
38Qdrant 내부 전용Qdrant 포트 외부 미노출
39볼륨 권한 관리open-webui-data 볼륨 소유권 관리

⚙️ Tools-API (4개)

#항목설명
40API Key 인증TOOLS_API_KEY 환경변수 기반 인증
41포트 로컬 바인딩127.0.0.1:8000 — 외부 접근 차단
42입력값 검증Tool 파라미터 타입·길이 검증
43응답 필터링민감정보 제거 후 반환

🌐 Browser Agent v7 (21개)

기존 14개

#항목설명
44API Key 인증 (hmac)BROWSER_AGENT_API_KEY hmac.compare_digest 검증
45SlowAPI Rate Limit엔드포인트별 분당 요청 횟수 제한
46CORS 제한허용 Origin 화이트리스트
47TrustedHost 미들웨어신뢰된 호스트 헤더만 허용
48보안 헤더 6종X-Frame-Options · X-Content-Type · CSP 등
49요청 본문 10KB 제한과도한 요청 본문 차단
50URL 차단 패턴내부망·localhost·위험 도메인 접근 차단
51Prompt Injection 감지브라우저 작업 태스크에서 명령 주입 패턴 차단
52타임아웃 3종task(180s) · multi(300s) · step(30s) 제한
53동시 실행 제한MAX_CONCURRENT=3 — 동시 브라우저 3개 이하
54Seccomp 프로파일허용 syscall 화이트리스트 (seccomp-browser.json)
55cap_drop + read_only모든 Capabilities 제거 · 루트 파일시스템 읽기 전용
56non-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만 접근 — 비관리자 전면 차단
66Rate Limiting분당 30회 제한 + 3회 실패 시 점진적 차단 (10→30→60→120분)
67Webhook 서명 검증Telegram Secret Token으로 요청 위조 방지
68통화 인증 (번호 기반)등록된 관리자 번호만 통화 허용 · PIN 방식 폐지로 유출 위험 제거
69입력 길이 제한4096자 제한 + null byte 제거 + XSS 방어
70민감정보 로그 마스킹API Key · Bearer 토큰 · 전화번호 자동 마스킹
71non-root 컨테이너UID 1001 botuser · /sbin/nologin 셸
72cap_drop + read_only모든 Linux 권한 제거 · 루트 파일시스템 읽기 전용
73파일 업로드 제한20MB 상한 · Path Traversal 방어 · 파일명 정규화
74세션 타임아웃30분 비활동 시 세션 초기화 · 세션 수 100개 상한
75Docker SecretsBot Token · API Key · Webhook Secret 파일 분리 저장
76.env chmod 600소유자만 읽기/쓰기 · 자동 백업 생성
77Model/Tool ID 검증정규식 화이트리스트 패턴 + 128자 길이 제한
78네트워크 격리Docker 내부 네트워크 · 포트 127.0.0.1 바인딩
79로그 로테이션10MB × 3파일 자동 순환
80Health Check 자동 복구30초 간격 · Restarting 감지 시 권한 자동 복구
81허용 사용자 영구 저장/app/data/allowed_users.json · chmod 600
82MAX_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 오류 우회 (재설치 시 자동 적용)
⚠️ WSL2 환경 주의: Seccomp 프로파일(항목 54·90)은 WSL2 커널의 openat2 미지원 문제로 인해 자동으로 privileged: true 모드로 대체됩니다. PowerShell(관리자)에서 wsl --update 실행 후 재설치하면 Seccomp 정상 적용됩니다.
APPENDIX

📂디렉토리 구조

~/
├── 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              # 설치 검증 스크립트