diff --git a/Dockerfiles/OllamaServer/Dockerfile b/Dockerfiles/OllamaServer/Dockerfile index 35f110f..b5fd995 100644 --- a/Dockerfiles/OllamaServer/Dockerfile +++ b/Dockerfiles/OllamaServer/Dockerfile @@ -1,6 +1,10 @@ # Используем готовый образ Ollama FROM localhost/ollama_packed:latest +RUN apt-get update && apt-get install -y --no-install-recommends bash && apt-get install -y --no-install-recommends curl \ +&& apt-get install -y --no-install-recommends jq && apt-get install -y --no-install-recommends wget + + # Устанавливаем bash если его нет (базовый образ ollama может быть на разных дистрибутивах) RUN if ! command -v bash >/dev/null 2>&1; then \ if command -v apk >/dev/null 2>&1; then \ @@ -72,6 +76,8 @@ ENV LCG_SERVER_HOST=0.0.0.0 ENV LCG_SERVER_PORT=8080 ENV LCG_DOMAIN="remote.ollama-server.ru" ENV LCG_COOKIE_PATH="/lcg" +# ENV LCG_FORCE_NO_CSRF=true + # ENV LCG_SERVER_ALLOW_HTTP=true # ENV OLLAMA_HOST=127.0.0.1 # ENV OLLAMA_PORT=11434 diff --git a/Dockerfiles/OllamaServer/Makefile b/Dockerfiles/OllamaServer/Makefile index 451cb51..19edb54 100644 --- a/Dockerfiles/OllamaServer/Makefile +++ b/Dockerfiles/OllamaServer/Makefile @@ -52,7 +52,7 @@ run: ## Запустить контейнер (Docker) -v lcg-results:/app/data/results \ -v lcg-prompts:/app/data/prompts \ -v lcg-config:/app/data/config \ - ${IMAGE_NAME}:${IMAGE_TAG} + ${IMAGE_NAME}:${IMAGE_TAG} ollama serve @echo "Контейнер ${CONTAINER_NAME} запущен" run-podman: ## Запустить контейнер (Podman) @@ -69,7 +69,7 @@ run-podman: ## Запустить контейнер (Podman) -v lcg-results:/app/data/results \ -v lcg-prompts:/app/data/prompts \ -v lcg-config:/app/data/config \ - ${IMAGE_NAME}:${IMAGE_TAG} + ${IMAGE_NAME}:${IMAGE_TAG} ollama serve @echo "Контейнер ${CONTAINER_NAME} запущен" run-podman-nodemon: ## Запустить контейнер (Podman) без -d @@ -86,7 +86,7 @@ run-podman-nodemon: ## Запустить контейнер (Podman) без -d -v lcg-results:/app/data/results \ -v lcg-prompts:/app/data/prompts \ -v lcg-config:/app/data/config \ - ${IMAGE_NAME}:${IMAGE_TAG} + ${IMAGE_NAME}:${IMAGE_TAG} ollama serve @echo "Контейнер ${CONTAINER_NAME} запущен" stop: ## Остановить контейнер (Docker) diff --git a/Dockerfiles/OllamaServer/entrypoint.sh b/Dockerfiles/OllamaServer/entrypoint.sh index 6dcf3ab..f5d7e81 100755 --- a/Dockerfiles/OllamaServer/entrypoint.sh +++ b/Dockerfiles/OllamaServer/entrypoint.sh @@ -65,7 +65,8 @@ export LCG_CONFIG_FOLDER="${LCG_CONFIG_FOLDER:-/app/data/config}" export LCG_SERVER_HOST="${LCG_SERVER_HOST:-0.0.0.0}" export LCG_SERVER_PORT="${LCG_SERVER_PORT:-8080}" export LCG_SERVER_ALLOW_HTTP="${LCG_SERVER_ALLOW_HTTP:-false}" -export LCG_FORCE_NO_CSRF="${LCG_FORCE_NO_CSRF:-true}" +export LCG_FORCE_NO_CSRF="${LCG_FORCE_NO_CSRF:-false}" +export LCG_CSRF_DEBUG_FILE="${LCG_CSRF_DEBUG_FILE:-/app/data/csrf-debug.log}" if [ "$LCG_FORCE_NO_CSRF" = "true" ]; then info "CSRF проверка отключена через LCG_FORCE_NO_CSRF" diff --git a/VERSION.txt b/VERSION.txt index 5d04a20..00f9e39 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -v.2.0.21 +v.2.0.25 diff --git a/deploy/VERSION.txt b/deploy/VERSION.txt index 5d04a20..00f9e39 100644 --- a/deploy/VERSION.txt +++ b/deploy/VERSION.txt @@ -1 +1 @@ -v.2.0.21 +v.2.0.25 diff --git a/kustomize/configmap.yaml b/kustomize/configmap.yaml index ae5c85a..bb134b1 100644 --- a/kustomize/configmap.yaml +++ b/kustomize/configmap.yaml @@ -5,7 +5,7 @@ metadata: namespace: lcg data: # Основные настройки - LCG_VERSION: "v.2.0.21" + LCG_VERSION: "v.2.0.25" LCG_BASE_PATH: "/lcg" LCG_SERVER_HOST: "0.0.0.0" LCG_SERVER_PORT: "8080" diff --git a/kustomize/deployment.yaml b/kustomize/deployment.yaml index 7e9aaf0..e7978d4 100644 --- a/kustomize/deployment.yaml +++ b/kustomize/deployment.yaml @@ -5,7 +5,7 @@ metadata: namespace: lcg labels: app: lcg - version: v.2.0.21 + version: v.2.0.25 spec: replicas: 1 selector: @@ -18,7 +18,7 @@ spec: spec: containers: - name: lcg - image: kuznetcovay/lcg:v.2.0.21 + image: kuznetcovay/lcg:v.2.0.25 imagePullPolicy: Always ports: - containerPort: 8080 diff --git a/kustomize/kustomization.yaml b/kustomize/kustomization.yaml index 83f9046..9b4ec2b 100644 --- a/kustomize/kustomization.yaml +++ b/kustomize/kustomization.yaml @@ -15,11 +15,11 @@ resources: # Common labels # commonLabels: # app: lcg -# version: v.2.0.21 +# version: v.2.0.25 # managed-by: kustomize # Images # images: # - name: lcg # newName: kuznetcovay/lcg -# newTag: v.2.0.21 +# newTag: v.2.0.25 diff --git a/serve/csrf.go b/serve/csrf.go index 569f78c..12232ec 100644 --- a/serve/csrf.go +++ b/serve/csrf.go @@ -8,6 +8,8 @@ import ( "fmt" "net/http" "os" + "path/filepath" + "sync" "time" "github.com/direct-dev-ru/linux-command-gpt/config" @@ -20,10 +22,72 @@ const ( CSRFTokenLifetimeSeconds = CSRFTokenLifetimeHours * 60 * 60 ) -// csrfDebugPrint выводит отладочную информацию только если включен debug режим -func csrfDebugPrint(format string, args ...interface{}) { +var ( + // csrfDebugFile файл для отладочного вывода CSRF + csrfDebugFile *os.File + // csrfDebugFileMutex мьютекс для безопасной записи в файл + csrfDebugFileMutex sync.Mutex +) + +// initCSRFDebugFile инициализирует файл для отладочного вывода CSRF +func initCSRFDebugFile() error { + debugFile := os.Getenv("LCG_CSRF_DEBUG_FILE") + if debugFile == "" { + return nil // Файл не указан, ничего не делаем + } + + // Создаем директорию для файла, если нужно + dir := filepath.Dir(debugFile) + if dir != "." && dir != "" { + if err := os.MkdirAll(dir, 0755); err != nil { + return fmt.Errorf("failed to create directory for CSRF debug file %s: %v", dir, err) + } + } + + // Создаем/перезаписываем файл + file, err := os.Create(debugFile) + if err != nil { + return fmt.Errorf("failed to create CSRF debug file %s: %v", debugFile, err) + } + + csrfDebugFileMutex.Lock() + // Закрываем старый файл, если был открыт + if csrfDebugFile != nil { + csrfDebugFile.Close() + } + csrfDebugFile = file + csrfDebugFileMutex.Unlock() + + // Записываем заголовок + header := fmt.Sprintf("=== CSRF Debug Log Started at %s ===\n", time.Now().Format(time.RFC3339)) + if _, err := csrfDebugFile.WriteString(header); err != nil { + return fmt.Errorf("failed to write header to CSRF debug file: %v", err) + } + if err := csrfDebugFile.Sync(); err != nil { + return fmt.Errorf("failed to sync CSRF debug file: %v", err) + } + + return nil +} + +// csrfDebugPrint выводит отладочную информацию +// Если установлен LCG_CSRF_DEBUG_FILE - всегда пишет в файл (независимо от debug режима) +// Если включен debug режим - также пишет в консоль +func csrfDebugPrint(format string, args ...any) { + message := fmt.Sprintf(format, args...) + + // Записываем в файл, если он установлен + csrfDebugFileMutex.Lock() + if csrfDebugFile != nil { + csrfDebugFile.WriteString(message) + // Синхронизируем сразу для отладки (может быть медленно, но гарантирует запись) + csrfDebugFile.Sync() + } + csrfDebugFileMutex.Unlock() + + // Записываем в консоль, если включен debug режим if config.AppConfig.MainFlags.Debug { - fmt.Printf(format, args...) + fmt.Print(message) } } @@ -336,6 +400,11 @@ var csrfManager *CSRFManager // InitCSRFManager инициализирует глобальный CSRF менеджер func InitCSRFManager() error { + // Инициализируем файл для отладки CSRF, если указан LCG_CSRF_DEBUG_FILE + if err := initCSRFDebugFile(); err != nil { + return fmt.Errorf("failed to initialize CSRF debug file: %v", err) + } + var err error csrfManager, err = NewCSRFManager() return err diff --git a/serve/middleware.go b/serve/middleware.go index 4696a7d..fdc8811 100644 --- a/serve/middleware.go +++ b/serve/middleware.go @@ -138,19 +138,39 @@ func CSRFMiddleware(next http.HandlerFunc) http.HandlerFunc { // Получаем CSRF токен из cookie для сравнения csrfTokenFromCookie := GetCSRFTokenFromCookie(r) + valid := true if csrfTokenFromCookie != "" { csrfDebugPrint("[CSRF MIDDLEWARE] CSRF токен из cookie (первые 50 символов): %s...\n", safeSubstring(csrfTokenFromCookie, 0, 50)) if csrfTokenFromCookie != csrfToken { csrfDebugPrint("[CSRF MIDDLEWARE] ⚠️ ВНИМАНИЕ: Токен из cookie отличается от токена в запросе!\n") + valid = false } else { csrfDebugPrint("[CSRF MIDDLEWARE] ✅ Токен из cookie совпадает с токеном в запросе\n") + valid = true } } else { csrfDebugPrint("[CSRF MIDDLEWARE] ⚠️ ВНИМАНИЕ: CSRF токен не найден в cookie!\n") + valid = false } // Проверяем CSRF токен + + if !valid { + csrfDebugPrint("[CSRF MIDDLEWARE] ❌ ОШИБКА: Валидация CSRF токена не прошла!\n") + // Для API запросов возвращаем JSON ошибку + if isAPIRequest(r) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusForbidden) + w.Write([]byte(`{"success": false, "error": "Invalid CSRF token"}`)) + return + } + + // Для веб-запросов возвращаем ошибку + http.Error(w, "Invalid OR Empty CSRF token", http.StatusForbidden) + return + } + csrfManager := GetCSRFManager() if csrfManager == nil { csrfDebugPrint("[CSRF MIDDLEWARE] ❌ ОШИБКА: CSRF менеджер не инициализирован!\n") @@ -165,7 +185,7 @@ func CSRFMiddleware(next http.HandlerFunc) http.HandlerFunc { } csrfDebugPrint("[CSRF MIDDLEWARE] Вызов ValidateToken с токеном и sessionID: %s\n", sessionID) - valid := csrfManager.ValidateToken(csrfToken, sessionID) + valid = csrfManager.ValidateToken(csrfToken, sessionID) csrfDebugPrint("[CSRF MIDDLEWARE] Результат ValidateToken: %t\n", valid) if !valid {