Все подсистемы Galleon пишут один поток структурированных логов в stdout — формат JSON, одна запись на строку. Ротация и доставка в систему хранения логов — забота платформы развёртывания.
Настройка
Блок logging конфигурации:
| Ключ | По умолчанию | Назначение |
|---|---|---|
level | info | Порог логов Galleon: debug, info, warn, error |
format | json | json или text |
registry_level | warn | Отдельный порог для записей о работе с образами контейнеров |
События аудита при работе с образами контейнеров (загрузка и удаление
манифестов, блобов, тегов) пишутся всегда, независимо от registry_level.
Access-лог
Каждый запрос пишет одну запись msg="http request" с полями:
source (подсистема), method, path, status, size,
user, client_ip, duration, время и число запросов к базе
и завершающим request_id.
В user стоит аутентифицированная учётная запись;
у запроса без успешной аутентификации там -.
Тот же идентификатор Galleon возвращает клиенту в заголовке X-Request-ID.
По нему в логе находятся все записи одного запроса.
client_ip за обратным прокси корректен только при настроенном доверии
(Обратный прокси).
Ошибки самого HTTP-сервера пишутся с источником server.http на уровне warn.
Когда Galleon отдаёт HTTPS сам, в этот источник попадают неудачные
TLS-рукопожатия (TLS).
Поля, которые появляются только на проблемном трафике:
sqlstate— запрос столкнулся с ошибкой PostgreSQL;served_stale=true— proxy-репозиторий отдал устаревшую копию, потому что внешний реестр не ответил;deny_reason— отказ аутентификации или доступа; словарь значений ниже.
Поле attempted_user — логин, предъявленный клиентом: учётные данные отвергнуты
или не проверялись, потому что запрос отклонён раньше. Значение ? —
авторизация не Basic или пустой логин. Неудачные входы ищите по этому полю:
в user у таких записей -. Поле появляется и на обычном трафике,
например когда мониторинг опрашивает открытый эндпоинт с учётными данными.
Причины отказов
Поле deny_reason объясняет каждый отказ, не меняя ответ клиенту
(коды ответов не раскрывают существование чужих репозиториев):
| Значение | Причина |
|---|---|
no_credentials | Запрос без учётных данных там, где они обязательны |
bad_credentials | Учётные данные предъявлены и отвергнуты |
anonymous_disabled | Анонимный доступ выключен |
anonymous_forbidden | Роли anonymous не хватает привилегии |
forbidden | Пользователю не хватает привилегии на репозиторий |
admin_required | Не хватает административной привилегии |
anon_no_admin | Анонимный запрос к образам контейнеров, требующий административных прав |
password_change_required | Заблокировано до принудительной смены пароля |
repository_not_found | Целевой репозиторий не найден; ставится и на ответ 404 до проверки учётных данных |
repository_name_invalid | Имя репозитория не разобрано, поиск не выполнялся |
wrong_format | Репозиторий существует, но другого формата |
monitoring_ip_denied | Адрес вне monitoring_allowlist |
upstream_manually_blocked | Обращения к внешнему реестру заморожены вручную |
upstream_auto_blocked | Обращения приостановлены автоблокировкой |
Запись с 401/403 без deny_reason — признак инфраструктурного сбоя:
смотрите поля err и sqlstate. Ответ 404 по репозиторию тоже несёт причину;
без неё остаются ответы репозитория, выведенного из обслуживания,
и отказы из-за неверной формы запроса.
События аудита
Административные и доменные события пишутся на уровне info:
- публикации артефактов — записи
msg="<FORMAT> artifact published"с координатами и репозиторием, по одной на артефакт; - операции с образами контейнеров — загрузка и удаление манифестов, блобов и тегов;
- инвалидация кеша репозитория (
source=audit.repository); - создание bootstrap-администратора, создание стартового набора репозиториев, итоги циклов сборки мусора, смены состояния соединений с внешними реестрами.
Секреты в лог не попадают ни на каком уровне:
значения заголовков авторизации, пароли, токены и содержимое тел не логируются.
Управляющие символы и некорректный UTF-8 в значениях, пришедших от клиента
(путь, заголовки, имена файлов), заменяются в логе на ?.