Выдать приложению доступ к одному бакету

Политика на конкретный бакет вместо readwrite на весь кластер, и почему у write-only не должно быть ListBucket

cat > /tmp/policy-ro.json <<'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetBucketLocation", "s3:ListBucket"],
      "Resource": ["arn:aws:s3:::backup"]
    },
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject"],
      "Resource": ["arn:aws:s3:::backup/*"]
    }
  ]
}
EOF
mc admin policy create s3 backup-ro /tmp/policy-ro.json
cat > /tmp/policy-wo.json <<'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:PutObject"],
      "Resource": ["arn:aws:s3:::artifacts/*"]
    }
  ]
}
EOF
mc admin policy create s3 artifacts-wo /tmp/policy-wo.json
mc admin policy attach s3 backup-ro --user app_ro
mc admin user svcacct add s3 app_ro --name app-ro-sa --policy /tmp/policy-ro.json
mc admin policy info s3 backup-ro
mc admin user info s3 app_ro

Ключевая деталь, из-за которой политики пишут руками, а не берут готовый readwrite: Resource для операций над бакетом и над объектами — разные ARN. arn:aws:s3:::backup — сам бакет, arn:aws:s3:::backup/* — объекты в нём. ListBucket относится к первому, GetObject и PutObject — ко второму. Если перепутать, политика применится без ошибок и молча не будет работать.

Матрица, по которой удобно собирать:

Режим ListBucket GetObject PutObject DeleteObject
read-only да да нет нет
read-write да да да да
write-only нет нет да нет

У write-only ListBucket отсутствует намеренно: агент, который складывает артефакты или логи, не должен иметь возможности перечислить чужие. Это ровно тот случай, когда «дадим на всякий случай» превращает односторонний канал в утечку содержимого хранилища.

Сервисный аккаунт с --policy предпочтительнее пользователя с паролем: он наследует права родителя, ограничивается своей политикой и отзывается одной командой, не задевая остальных потребителей. Но родителем стоит делать пользователя с узкой политикой, а не admin — иначе «сервисный» ключ упирается в права root.