ActiveScaleинженерная практика

S3 AccessDenied на листинг: у аккаунта нет resource policy

Диагностика s3iamaccess-deniedresource-policy403troubleshoot

Коротко. Ключи и аккаунт созданы, бакет существует, GUI зелёный — а клиент получает 403 AccessDenied уже на листинге. Причина в правах: у ресурса нет resource policy, которая разрешает этому аккаунту доступ. Штатный путь — выдать политику через iamd (PutResourcePolicy), а не обходить проверку системным ключом.


Симптом

  • Клиент получает 403 AccessDenied на ListBucket, GET, PUT — иногда на всё сразу.
  • Тело ответа — код AccessDenied, а не SignatureDoesNotMatch и не текст про разницу времени.
  • Ключи заведомо верные: тот же аккаунт работает из консоли продукта, либо ключи только что созданы и проверены.
  • Аккаунта нет в /model/ — и это норма: аккаунты и ключи живут в iamd, а не в модели.
  • В GUI ошибок по узлам, дискам и службам нет; S3-фронт отвечает (например 307 на корень).
  • Отдельно похожий, но другой случай: 403 на запрос с неподписанным x-amz-meta-* — это штатное правило S3 («заголовки x-amz-* надо подписывать»), а не отсутствие политики.

Что проверить первым (read-only)

Проверки — через консоль продукта: sudo -i; sherpa -c "<одна строка>". gRPC-вызовы iamd требуют явных аргументов, поэтому поля перечисляются все.

  1. Есть ли аккаунт и пользователь: python from google.protobuf.json_format import MessageToDict iam = cli._connection_factory.create_iamd_connection() MessageToDict(iam.ListAccounts(marker='', onlyIfQuotaExceeded=False, nameOrEmailRegex='', maxItems=100, allowDisabled=True)) MessageToDict(iam.ListUsers(accountEmail='<ACCOUNT_EMAIL>', maxItems=100, marker='', pathPrefix='', allowDisabled=True, nameOrEmailRegex='', accountCanonicalId='')) Ожидается: аккаунт в списке, пользователь активен, ключ существует. Если аккаунта нет — это другой класс проблемы (учётку не создали), а не права на ресурс.

  2. Проверка вендорским путём, без внешнего клиента (cli.s3_connect сам берёт секрет из iamd): python conn = cli.s3_connect('<ACCESS_KEY_ID>') b = conn.get_bucket('<BUCKET>', validate=False) # validate=False обязателен print b.get_all_keys() Ожидается список объектов. S3ResponseError: 403 AccessDenied — подтверждение, что дело в правах на ресурс, а не в ключах или сети.

  3. Что говорит политика бакета (подписанным запросом валидными ключами): bash aws --endpoint-url https://<DOMAIN> s3api get-bucket-policy --bucket <BUCKET> Ожидается документ политики с нужным principal и действиями. Пустой ответ / отсутствие политики  — причина найдена. У продукта bucket policy API есть: PUT ?policy отвечает 204, GET ?policy отдаёт политику.

  4. Доступен ли IAM по имени, а не по адресу: IAM вызывается по IAM-домену; обращение «по IP» не работает. Проверяется резолвом домена и тем, что ListAccounts из п.1 отвечает, а не висит.

  5. Отвечает ли фронт вообще (чтобы исключить TLS/DNS): bash curl -sS -o /dev/null -w '%{http_code}\n' https://<DOMAIN>/ Ожидается 307 и Server: Quantum-ActiveScale.


Причина

Права проверяются после подписи

Шлюз принимает запрос, проверяет подпись SigV4, затем — права. Аккаунт с валидными ключами, но без разрешения на конкретный ресурс, получает AccessDenied. Это не ошибка ключей: подпись сошлась, иначе клиент увидел бы SignatureDoesNotMatch.

Почему «аккаунт есть» не означает «доступ есть»

Создание аккаунта и ключа не создаёт разрешений на бакеты. Разрешение — отдельный документ resource policy, привязанный к ARN ресурса (arn:aws:s3:::<BUCKET>), где перечислены principal и действия. Пока документа нет, шлюз по умолчанию закрывает доступ даже на листинг. Продукт ведёт себя как S3: «запрещено, пока не разрешено явно».

Где путаются

  • Аккаунты не в модели. Поиск аккаунта в /model/ даёт пусто и рождает ложную гипотезу «аккаунт потерялся». Аккаунты — в iamd (gRPC), проверяются через ListAccounts.
  • Principal: "*" не открывает анонимный доступ. Политика бакета с Principal: "*" и {"AWS": "*"} на практике отдаёт анонимным запросам 403 AccessDenied; анонимов пускает ACL public-read-write, но при анонимной записи шлюз молча выбрасывает x-amz-meta-*. Это другой сценарий и другая статья; для доступа по ключам нужен principal конкретного аккаунта.
  • Системный foos-ключ. Обход прав «служебным ключом» — не ремонт, а дыра; так делать нельзя.

Что здесь гипотеза

Механизм «нет resource policy» подтверждён живыми вызовами. Если политика есть и разрешает нужные действия, а 403 AccessDenied остаётся — гипотеза: не совпал principal (canonical id аккаунта против указанного в документе) либо ARN указывает не на тот ресурс. Проверяется сверкой документа политики с canonical id аккаунта и точным именем бакета.


Решение (штатное)

  1. Зафиксировать текущее состояние: список аккаунтов/пользователей (п.1) и документ политики, если он есть (п.3). Скопировать документ в файл вне каталога продукта — это основа отката.

  2. Подготовить документ политики под конкретный аккаунт и бакет: principal — аккаунт, действия — минимум необходимого (s3:ListBucket, s3:GetObject, s3:PutObject — по задаче), ресурсы — бакет и arn:aws:s3:::<BUCKET>/*.

  3. Выдать политику штатным путём через iamd: python iam = cli._connection_factory.create_iamd_connection() iam.PutResourcePolicy(resourceArn='arn:aws:s3:::<BUCKET>', resourcePolicyDocument=<JSON_DOC>, expectedResourceId='') Ожидается пустой ответ {} = успех. Имя поля — именно resourcePolicyDocument; другое имя даёт ошибку вызова, а не «политика не применилась».

  4. Проверить доступ тем же вендорским путём (п.2): get_bucket(..., validate=False) и get_all_keys() без исключения.

  5. Проверить внешним клиентом: aws s3 ls, затем PUT/GET небольшого объекта. Ожидается 200 и совпадающее тело.

  6. Записать, что именно выдано (ресурс, principal, действия) — в реестр доступа; это пригодится при аудите и при следующем AccessDenied.

Если штатный путь недоступен

  • Нет доступа к sherpa (L1 без прав root): собрать факты (код ответа, ключ, бакет, время), передать инженеру. Временная мера «дать полный доступ всем» недопустима.
  • Нужен доступ сегодня, а политику согласуют долго: временная мера — отдельный аккаунт с минимальной политикой под конкретную задачу; пометка не штатно в смысле «обход процесса согласования», план замены — штатная политика на основной аккаунт.
  • Обход системным ключом/отключение проверки прав — запрещено: это не решение, а неконтролируемый доступ.

Проверка

  • PutResourcePolicy → {} (успех).
  • cli.s3_connect('<ACCESS_KEY_ID>') → get_bucket('<BUCKET>', validate=False) без ошибки; get_all_keys() возвращает список.
  • aws s3 ls → листинг; PUT/GET объекта → 200, тело совпадает.
  • GET ?policy → документ содержит нужный principal и действия.
  • 403 с кодом AccessDenied в логах шлюза по этому аккаунту больше не появляется.
  • Контроль: 403 на запрос с неподписанным x-amz-meta-* остаётся — это норма, не регресс.

Профилактика

  • Выдавать политику вместе с аккаунтом — в одном чек-листе провижининга, а не «потом».
  • Проверять доступ вендорским путём (cli.s3_connect) сразу после создания ключей: дешевле, чем ждать клиента.
  • Различать коды: AccessDenied — права; SignatureDoesNotMatch — секрет/подпись; текст про разницу времени — часы; TLS-ошибка — сертификат и DNS. Лечение у них разное.
  • Не искать аккаунты в модели — они в iamd.
  • Реестр доступов: кто, к какому бакету, какие действия. При аудите и разборе 403 это первое, что спрашивают.

Источники и дата

  • Вендор: Управление системой (GUI) (дата доступа 2026-11-07)
  • Вендор: Software Documentation Center 7.3 (дата доступа 2026-11-07)
  • Наша практика (обезличено): класс «S3 AccessDenied — нет resource policy у аккаунта»; выдача через iamd, проверка вендорским S3-подключением.
  • Обновлено: 2026-11-07.

Нужен инженер? Разберу вашу ситуацию и скажу, что делать — по шагам, с проверками.

Обсудить задачу