• Русский
  • На странице проекта отображается «Forbidden» из-за сбоя поиска по имени

    Описание проблемы

    Для некоторых проектов Harbor в веб-интерфейсе при доступе к репозиториям или другим сведениям о проекте отображается ошибка forbidden. API возвращает 403:

    {"errors":[{"code":"FORBIDDEN","message":"forbidden"}]}

    Другие проекты работают нормально. Проблема может проявиться после обновления Harbor.

    Корневая причина

    Имя проекта, сохраненное в таблице базы данных project, было повреждено (например, из-за невидимых символов или проблем с кодировкой). Когда Harbor запрашивает проект по имени через ORM:

    SELECT * FROM project WHERE name = '<project_name>' AND deleted = false;

    Проверка на равенство не проходит, и запрос не возвращает ни одной строки, хотя запись существует и может быть найдена по project_id. Поскольку поиск проекта завершается неудачей, проверка прав возвращает false, и API отвечает 403 Forbidden вместо фактических данных.

    Связанная upstream-проблема: https://github.com/goharbor/harbor/issues/15620

    Диагностика

    Шаг 1. Проверьте логи Harbor Core

    Найдите следующий шаблон ошибки в логах harbor-core:

    [ERROR] [/server/v2.0/handler/base.go:87]: failed to get project <project_name>: project <project_name> not found

    Полная цепочка логов обычно выглядит так:

    [DEBUG] get project <project_name> from cache error: key not found:redis: nil, will query from database.
    [ERROR] [/server/v2.0/handler/base.go:87]: failed to get project <project_name>: project <project_name> not found
    [DEBUG] {"errors":[{"code":"FORBIDDEN","message":"forbidden"}]}

    Шаг 2. Проверьте базу данных

    Подключитесь к базе данных Harbor (registry):

    kubectl exec -it <harbor-database-pod> -n <namespace> -- psql -d registry

    Выполните следующие запросы:

    -- Запрос по project_id — должен вернуть строку
    SELECT project_id, name, deleted FROM project WHERE project_id = <id>;
    
    -- Запрос по имени — возвращает 0 строк, если проблема существует
    SELECT project_id, name, deleted FROM project WHERE name = '<project_name>';

    Если первый запрос возвращает проект, а второй — 0 строк, проблема подтверждена.

    Решение

    В базе данных Harbor (registry) перепишите поле name, используя project_id:

    -- Исправление поврежденного имени
    UPDATE project SET name = '<project_name>' WHERE project_id = <id>;
    
    -- Перестроение индексов для обеспечения согласованности (см. замечания о рисках ниже)
    REINDEX TABLE project;

    Проверьте исправление:

    SELECT project_id FROM project WHERE name = '<project_name>' AND deleted = false;
    -- Должна быть возвращена 1 строка

    Обновите веб-интерфейс Harbor, чтобы убедиться, что ошибка 403 устранена.

    Примечания о рисках REINDEX

    REINDEX TABLE project перестраивает все индексы в таблице project. Это неразрушающая операция (она не изменяет строки данных), однако следует учитывать некоторые риски:

    ЭлементВлияние
    Блокировка записиВо время перестроения индекса таблица получает блокировку ACCESS EXCLUSIVE, которая блокирует все операции чтения и записи (SELECT/INSERT/UPDATE/DELETE) для этой таблицы до завершения операции.
    ДлительностьДля небольших таблиц (например, с десятками строк) операция завершается за миллисекунды. Для больших таблиц длительность блокировки соответственно увеличивается.
    Влияние на соединенияЕсли другие компоненты Harbor (core, jobservice и т. д.) попытаются запросить таблицу project во время REINDEX, эти запросы будут поставлены в очередь и могут выглядеть как кратковременные зависания или тайм-ауты.
    Безопасность при сбоеЕсли REINDEX будет прерван (например, из-за перезапуска Pod или обрыва соединения), PostgreSQL автоматически выполнит откат. Исходные индексы останутся целыми и пригодными к использованию — потеря или повреждение данных не произойдет.
    Место на дискеДля перестроения индексов временно требуется дополнительное место на диске, чтобы создать новые индексы перед удалением старых. Убедитесь, что на томе базы данных достаточно свободного места.

    Рекомендации:

    • В производственной среде выполняйте операцию в окно обслуживания с низкой нагрузкой, чтобы минимизировать влияние блокировки записи.
    • Для таблицы project объем данных обычно очень мал (десятки или сотни строк), поэтому длительность блокировки, как правило, пренебрежимо мала.
    • Если требуется отсутствие простоя, PostgreSQL 12+ поддерживает REINDEX TABLE CONCURRENTLY project, который строит новый индекс без установки эксклюзивной блокировки. Однако эта операция занимает больше времени и требует больше дискового пространства.