Чтобы подключить AWS CLI к S3-совместимому Object Storage стороннего провайдера, нужны четыре основных параметра:
- Access Key;
- Secret Key;
- endpoint;
- регион.
Лучше сразу создать отдельный профиль: aws configure --profile storage
Проверить подключение можно безопасной командой чтения:
aws s3 ls \
--endpoint-url https://s3.example-provider.com \
--profile storageСоздать bucket:
aws s3 mb s3://my-bucket \
--endpoint-url https://s3.example-provider.com \
--profile storageЗагрузить файл:
aws s3 cp ./file.txt s3://my-bucket/file.txt \
--endpoint-url https://s3.example-provider.com \
--profile storageСкачать его обратно:
aws s3 cp s3://my-bucket/file.txt ./file.txt \
--endpoint-url https://s3.example-provider.com \
--profile storageСинхронизировать каталог:
aws s3 sync ./data s3://my-bucket/data/ \
--endpoint-url https://s3.example-provider.com \
--profile storageПеред использованием --delete сначала проверьте результат через --dryrun:
aws s3 sync ./data s3://my-bucket/data/ \
--delete \
--dryrun \
--endpoint-url https://s3.example-provider.com \
--profile storageЕсли AWS CLI возвращает ошибку, проверяйте по порядку: endpoint, профиль и credentials, регион, системное время, права policy и addressing style.
Для production не храните Secret Key прямо в скриптах, используйте отдельные service accounts с минимальными правами и заранее продумайте ротацию ключей.
Что такое S3-совместимое Object Storage и чем оно отличается от обычного диска
Добро пожаловать!
Сегодня разберем одну из тех технологий, которые часто используют буквально каждый день, но при этом не всегда до конца понимают, что именно происходит под капотом.
Речь про S3-совместимое объектное хранилище.
На первый взгляд всё выглядит очень знакомо: есть файлы, есть что-то похожее на папки, можно загружать данные, скачивать их обратно и даже синхронизировать целые каталоги.
Из-за этого возникает естественная мысль: «Ну то есть это просто удаленный диск?»
Не совсем.
Object Storage устроено иначе, чем обычная файловая система, сетевой диск или раздел на VPS. И это различие важно, потому что именно оно объясняет почти всё остальное: почему здесь появляются bucket, object key, endpoint, подписи запросов и отдельные правила доступа.
В этой статье мы будем работать через AWS CLI, но подключаться не обязательно к S3 от Amazon . Наша задача шире — понять, как использовать S3-совместимое хранилище стороннего провайдера через собственный endpoint.
То есть в итоге мы научимся:
- Подключаться к хранилищу через AWS CLI;
- Работать с отдельными профилями;
- Создавать и просматривать bucket;
- Загружать и скачивать объекты;
- Синхронизировать каталоги;
- Управлять доступом;
- Диагностировать ошибки вроде AccessDenied и SignatureDoesNotMatch.
Но прежде чем переходить к командам, стоит нормально разобраться с самой моделью хранения.
Почему Object Storage — не файловая система
Обычная файловая система строится вокруг каталогов, файлов и иерархии.
Например: /home/user/photos/cat.jpg
Тут есть:
- Каталог /home;
- Внутри него подкаталог user;
- Затем photos;
- И уже там файл cat.jpg.
Файловая система действительно знает, что каталоги существуют как отдельные сущности.
У объектного хранилища логика другая.
В S3-подобной модели данные хранятся как объекты, а каждый объект имеет уникальный ключ.
Например: photos/cat.jpg может выглядеть как путь, но технически это всего лишь строка, которая является ключом объекта.
То есть объектное хранилище не обязано воспринимать photos/ как настоящую директорию.
Это скорее префикс.
Именно поэтому можно встретить объект backup/2026/09/database.sql, даже если никакие отдельные каталоги backup, 2026 или 09 заранее не создавались.
Для человека интерфейс может показывать это как папки, потому что так удобнее воспринимать структуру. Но на уровне API чаще всего речь идет просто о ключах с общими префиксами.
Это одна из главных особенностей Object Storage.
Отсюда вытекают и другие отличия.
Например, обычный диск удобно использовать для:
- Файлов, к которым приложение обращается постоянно;
- Баз данных;
- Системных каталогов;
- Рабочих директорий;
- Файлов с частыми мелкими изменениями.
Object Storage чаще используют для:
- Резервных копий;
- Архивов;
- Изображений;
- Видео;
- Документов;
- Статических файлов;
- Логов;
- Больших наборов данных.
Почему?
Потому что модель «объект целиком по ключу» отлично подходит для хранения данных, которые нужно надежно положить, получить и раздать через API. Как следствие, удобнее делать stateless-инфраструктуру, когда приложение не использует для данных локальные каталоги, а использует S3-сервис. Это облегчает горизонтальное масштабирование и упрощает микросервисную архитектуру.
Но она не пытается вести себя как локальная файловая система со всеми её привычными операциями.
А можно подключить S3 как обычную файловую систему?
Технически — да. Для этого существуют инструменты вроде s3fs, которые позволяют смонтировать S3-совместимое хранилище в Linux и обращаться к объектам через привычный путь в файловой системе.
Но важно понимать, что настоящий файловый диск из S3 при этом не появляется. s3fs работает через FUSE и преобразует файловые операции в запросы к S3 API. Поэтому привычные для локальной файловой системы действия могут работать иначе, медленнее или требовать дополнительных запросов к Object Storage.
Из-за этого такой вариант обычно не используют как полноценную замену локальному диску для баз данных, активно изменяемых файлов или приложений, которые рассчитывают на обычную POSIX-файловую систему. Он встречается скорее в legacy-сценариях, миграциях и случаях, когда существующее приложение трудно быстро переделать под нативную работу с S3 API.
Для новых приложений обычно надежнее работать с Object Storage как с объектным хранилищем: обращаться к bucket и object key через S3 API, AWS CLI или SDK.
Что такое bucket, object и key
Чтобы дальше не путаться, разберем три основных термина.
Bucket
Bucket — это верхнеуровневый контейнер для объектов.
Например: my-backups или site-media
Можно условно представить bucket как отдельное пространство хранения.
Но bucket — не просто обычная папка.
На его уровне могут задаваться:
- Правила доступа;
- Регион;
- versioning;
- lifecycle;
- Политики;
- Ограничения;
- Настройки публичности.
Набор возможностей зависит от конкретного S3-совместимого провайдера.
Object
Object — это собственно данные, которые мы загрузили.
Например: report.pdf или database.sql.gz
Объект обычно состоит не только из содержимого файла, но и из метаданных.
То есть S3 хранит не просто «набор байтов», а объект с дополнительной информацией.
Key
Key — это уникальное имя объекта внутри bucket.
Например: backups/db.sql или images/2026/avatar.png
Ключ может выглядеть как обычный путь, но это всё равно строка.
Именно по ключу объект находится внутри bucket.
Если у нас есть:
Bucket: my-backups
Key: daily/postgres.sql.gzто условный адрес объекта можно представить как: s3://my-backups/daily/postgres.sql.gz
Эта запись часто встречается в AWS CLI.
Например:
aws s3 cp backup.sql.gz s3://my-backups/daily/backup.sql.gzЗдесь my-backups — bucket, а daily/backup.sql.gz — key.
Именно это понимание потом сильно помогает при sync, cp --recursive и работе с префиксами.
Что означает «S3-compatible»
Теперь самое интересное.
S3 — это сервис Amazon Simple Storage Service.
Со временем его API стал настолько распространенным, что множество других облачных провайдеров начали реализовывать совместимый интерфейс.
Отсюда и появилось выражение: S3-compatible Object Storage.
То есть стороннее хранилище старается поддерживать API и модель взаимодействия, совместимые с Amazon S3.
Благодаря этому один и тот же клиент может работать с разными провайдерами.
Например, AWS CLI изначально создан для AWS, но при правильной настройке может отправлять запросы и в другое S3-совместимое хранилище.
Это удобно, потому что не нужно осваивать отдельную утилиту под каждого провайдера.
Можно использовать привычные команды:
aws s3 ls
aws s3 cp
aws s3 sync
и просто указать другой endpoint.
Но слово compatible не означает: «Это 100% точная копия Amazon S3 со всеми функциями и поведением».
У разных провайдеров могут отличаться:
- Поддержка ACL;
- bucket policy;
- versioning;
- object lock;
- lifecycle rules;
- Регионы;
- Ограничения имен;
- Способ формирования адресов;
- Некоторые API-возможности.
Поэтому S3-compatible — это скорее совместимость на уровне основного API и популярных операций, а не обещание полной идентичности.
Это особенно важно при переносе сложной инфраструктуры между провайдерами.
Простой cp обычно работает без проблем.
А вот редкие настройки вроде специфических bucket policies могут вести себя иначе.
Почему у стороннего провайдера свой endpoint
У Amazon S3 есть собственные адреса API.
У стороннего провайдера инфраструктура другая, поэтому запросы должны идти уже на его серверы.
Для этого используется endpoint.
Endpoint — это адрес API, к которому подключается клиент.
Например: https://s3.example-provider.com или https://object-storage.example.net
И вот здесь появляется ключевое различие между обычной работой с AWS и S3-compatible хранилищем.
Если выполнить: aws s3 ls без дополнительной настройки, AWS CLI по умолчанию ориентируется на инфраструктуру Amazon.
А нам нужно сказать: «Используй тот же S3 API, но отправляй запросы вот сюда».
Для этого применяется: --endpoint-url
Например:
aws s3 ls \
--endpoint-url https://s3.example-provider.comИменно endpoint определяет, куда физически уйдет API-запрос.
При этом Access Key и Secret Key определяют уже другую вещь — кто именно отправляет запрос.
Это важно разделять.
Условно:
| Что | За что отвечает |
| Endpoint | Куда отправляется запрос |
| Access Key | Какая учетная запись используется |
| Secret Key | Чем подписывается запрос |
| Region | Как формируется часть запроса и подписи |
| Bucket | С каким контейнером работаем |
| Key | С каким объектом внутри bucket работаем |
Именно из-за этого ошибка endpoint и ошибка ключей выглядят по-разному.
Если endpoint вообще недоступен, AWS CLI не сможет установить соединение.
Если endpoint правильный, но ключи неверны, запрос уже дойдет до сервера, а тот ответит ошибкой авторизации.
Дальше как раз разберем, как AWS CLI формирует такие запросы, зачем нужны Access Key и Secret Key, что такое Signature Version 4 и почему даже системное время сервера может неожиданно повлиять на авторизацию.
Как AWS CLI подключается к S3-совместимому хранилищу
Снаружи всё выглядит просто: мы вводим команду вроде: aws s3 ls и получаем список bucket.
Но между вводом команды и ответом сервера происходит несколько важных вещей. CLI должен понять, куда отправить запрос, какими учетными данными его подписать, какой регион учитывать и где вообще взять все эти параметры.
Именно поэтому при работе со сторонним S3-совместимым хранилищем чаще всего возникают не проблемы с самой командой cp или sync, а ошибки в endpoint, ключах или подписи запроса.
Зачем нужен собственный endpoint
У AWS CLI есть естественное ожидание: если ничего не уточнять, он будет работать с инфраструктурой Amazon.
Но сторонний провайдер — это другая инфраструктура, другой API-адрес и, возможно, другой регион.
Поэтому нужно явно указать endpoint.
Например: https://s3.example-provider.com
Endpoint — это адрес, куда AWS CLI отправляет S3-запросы.
Если использовать:
aws s3 ls \
--endpoint-url https://s3.example-provider.comCLI не меняет саму логику S3-команды. Он просто перенаправляет запрос на другой сервер.
Это и есть одна из сильных сторон S3-совместимости: клиент остается тем же, меняется только точка подключения.
Endpoint особенно важно не путать с адресом конкретного bucket.
Обычно endpoint — это адрес API сервиса, а bucket уже указывается отдельно: s3://my-bucket
В зависимости от провайдера запрос может затем формироваться в одном из вариантов: https://s3.example.com/my-bucket/object.txt или https://my-bucket.s3.example.com/object.txt
К этому различию мы еще вернемся, когда будем разбирать path-style и virtual-hosted-style запросы.
Как работают Access Key и Secret Key
Endpoint отвечает на вопрос: Куда отправить запрос?
Но хранилище еще должно понять, кто именно обращается к API.
Для этого используются две связанные учетные величины:
- Access Key ID;
- Secret Access Key.
Access Key можно условно представить как идентификатор учетной записи или сервисного пользователя.
Он не является секретом в том же смысле, что пароль, но всё равно не стоит разбрасывать его без необходимости.
Secret Key — уже чувствительный секрет.
Именно он используется для формирования криптографической подписи запроса.
При этом AWS CLI не отправляет Secret Key в открытом виде вместе с запросом.
Вместо этого он использует его как входные данные для вычисления подписи.
Сервер со своей стороны знает соответствующий секрет и может проверить, что подпись корректна.
Это важно, потому что схема не сводится к: логин + пароль в привычном веб-смысле.
Здесь каждый API-запрос подписывается.
Условно можно представить это так: «Я — пользователь с Access Key X. Вот запрос, который я отправляю. А вот криптографическая подпись, которая доказывает, что этот запрос сформирован обладателем правильного Secret Key».
Именно поэтому потеря Secret Key критична.
Если он утек, злоумышленник потенциально сможет подписывать запросы от имени вашей учетной записи до тех пор, пока ключ не будет отозван или заменен.
Что такое Signature Version 4
В большинстве современных S3-совместимых систем используется AWS Signature Version 4, или сокращенно SigV4.
Это механизм подписи API-запросов.
AWS CLI берет несколько элементов запроса:
- HTTP-метод;
- Путь;
- query-параметры;
- Заголовки;
- Время;
- Регион;
- Имя сервиса;
- Хэш содержимого.
Из этого формируется каноническое представление запроса.
Затем на основе Secret Key вычисляется подпись.
В итоге сервер получает не просто GET /bucket/file.txt, а подписанный запрос с дополнительными заголовками авторизации.
Смысл SigV4 в том, что сервер может проверить сразу несколько вещей:
- Кто отправил запрос;
- Не изменился ли запрос по дороге;
- Соответствует ли подпись конкретному региону и сервису;
- Не слишком ли старый запрос.
Именно поэтому ошибка в seemingly unrelated параметре может привести к: SignatureDoesNotMatch
Например, если endpoint правильный, а регион в конфигурации не совпадает с тем, который ожидает S3-провайдер, подпись может формироваться иначе.
То же самое может произойти, если клиент и сервер по-разному интерпретируют путь или host.
Это одна из причин, почему S3-compatible не всегда означает полное отсутствие нюансов.
Почему системное время влияет на авторизацию
Вот один из тех фактов, которые легко не учитывать.
Подписанный запрос содержит время.
Это сделано специально: сервер не должен принимать один и тот же корректно подписанный запрос бесконечно долго.
Поэтому если системные часы сильно отстают или спешат, сервер может решить, что запрос слишком старый или вообще пришел «из будущего».
Тогда можно получить ошибки вроде RequestTimeTooSkewed или похожие проблемы с подписью.
На нормальном VPS время обычно синхронизируется автоматически через NTP или systemd-timesyncd.
Проверить состояние можно через: timedatectl
Нас интересуют строки вроде:
System clock synchronized: yes
NTP service: activeЕсли время действительно сильно отличается, сначала стоит исправить синхронизацию, а уже потом искать проблему в ключах.
Это особенно полезный диагностический принцип: если ключи выглядят правильными, endpoint отвечает, а подпись всё равно не сходится — проверьте время и регион.
Где AWS CLI хранит настройки и credentials
AWS CLI обычно разделяет конфигурацию и секретные данные на два файла в домашнем каталоге пользователя.
Основные пути: ~/.aws/credentials и ~/.aws/config
В credentials обычно находятся Access Key и Secret Key.
Например:
[storage]
aws_access_key_id = ACCESS_KEY
aws_secret_access_key = SECRET_KEYА в config — параметры профиля:
[profile storage]
region = us-east-1
output = jsonЭто разделение удобно, потому что credentials можно защищать строже, а конфигурацию — менять отдельно.
Но есть важный нюанс: endpoint стороннего S3-провайдера не всегда хранится там автоматически после обычного aws configure.
Поэтому на практике чаще делают одно из двух:
- Каждый раз передают --endpoint-url;
- Либо настраивают endpoint через поддерживаемую конфигурацию конкретной версии AWS CLI.
Для учебной статьи мы сначала будем использовать явный --endpoint-url, потому что так нагляднее: видно, куда именно уходит запрос.
И еще один важный момент.
Файлы: ~/.aws/credentials и ~/.aws/config относятся к конкретному Linux-пользователю.
Если AWS CLI запускается от другого пользователя, например через sudo, он может искать уже: /root/.aws/, а не /home/ubuntu/.aws/
Из-за этого иногда возникает странная ситуация:
aws s3 ls
→ работает
sudo aws s3 ls
→ credentials не найдены
Проблема не в S3, а в том, что команда запущена в другом пользовательском окружении.
Мини-таблица команд
| Задача | Команда |
| Проверить установленную версию AWS CLI | aws --version |
| Посмотреть текущую конфигурацию | aws configure list |
| Посмотреть доступные профили | aws configure list-profiles |
| Проверить конкретный профиль | aws configure list --profile storage |
| Проверить системное время | timedatectl |
| Посмотреть каталог конфигурации | ls -la ~/.aws/ |
На этом этапе уже понятно, что подключение к S3 — это не просто «передать URL и пароль».
AWS CLI должен собрать несколько частей вместе: endpoint, credentials, регион, время и правила подписи.
Следующим шагом установим сам AWS CLI, создадим отдельный профиль для нашего хранилища и подготовим конфигурацию так, чтобы не смешивать ключи разных сервисов.
Устанавливаем AWS CLI и создаем отдельный профиль
Здесь важно не просто установить AWS CLI, а сразу настроить его так, чтобы работа с S3-совместимым хранилищем не смешивалась с другими учетными данными.
Это особенно полезно, если на одном сервере или рабочем компьютере позже будут одновременно использоваться:
- Amazon S3;
- Стороннее S3-совместимое хранилище;
- Отдельное backup-хранилище;
- Тестовый и production-аккаунты.
Вместо одной общей конфигурации для всего удобнее создать отдельный профиль.
Почему профиль удобнее глобальной конфигурации
AWS CLI умеет работать с несколькими наборами настроек.
Каждый такой набор называется profile.
Например:
default
storage
backup
production
У каждого профиля могут быть свои:
- Access Key;
- Secret Key;
- регион;
- формат вывода;
- дополнительные параметры.
Это позволяет явно говорить AWS CLI: используй именно этот набор credentials.
Например: aws s3 ls --profile storage или aws s3 ls --profile backup
Так намного сложнее случайно отправить команду не в то хранилище или использовать не те ключи.
Без профилей всё чаще сводится к одному глобальному набору параметров: default
А потом при появлении второго провайдера начинается путаница: какие ключи сейчас активны, какой регион выбран и почему вчера команда работала, а сегодня внезапно получает AccessDenied.
Поэтому для нашего S3-совместимого хранилища сразу создадим отдельный профиль: storage
Какие параметры нужно получить у провайдера
До настройки AWS CLI нужно получить несколько значений из панели облачного провайдера.
Обычно понадобятся:
| Параметр | Для чего нужен |
| Access Key ID | Идентифицирует учетную запись или сервисного пользователя |
| Secret Access Key | Используется для подписи запросов |
| Endpoint | Адрес S3 API конкретного провайдера |
| Region | Регион, который участвует в конфигурации и подписи |
| Bucket name | Понадобится позже для работы с конкретным bucket |
Названия полей в панели могут немного отличаться.
Например, вместо Access Key ID может использоваться: Access Key или S3 Access Key
То же самое относится к endpoint.
Он может выглядеть примерно так: https://s3.example-provider.com или включать регион: https://s3.eu.example-provider.com
Здесь важно брать значения именно из документации или панели вашего провайдера, а не копировать endpoint из чужой статьи.
S3 API совместим, а адрес инфраструктуры у каждого сервиса свой.
Где хранятся credentials и config
После настройки профиля AWS CLI создаст или обновит каталог: ~/.aws/
Внутри обычно находятся два файла.
Первый: ~/.aws/credentials
В нем хранятся ключи.
Например:
[storage]
aws_access_key_id = ACCESS_KEY
aws_secret_access_key = SECRET_KEYВторой: ~/.aws/config
В нем находятся остальные параметры профиля.
Например:
[profile storage]
region = us-east-1
output = jsonОбратите внимание на небольшое отличие синтаксиса.
В credentials профиль называется: [storage]
А в config: [profile storage]
Это нормальный формат AWS CLI.
Проверить созданные файлы можно так: ls -la ~/.aws/
Но выводить содержимое credentials в терминал без необходимости не стоит.
Почему ключи нельзя вставлять в скрипты
Secret Access Key нужно воспринимать примерно как пароль к API.
Если он утечет, злоумышленник может использовать его для подписания запросов от имени вашей учетной записи.
А последствия уже зависят от выданных прав.
Если ключ имеет широкий доступ, с его помощью потенциально можно:
- Читать объекты;
- Загружать новые;
- Перезаписывать существующие;
- Удалять данные;
- Просматривать buckets;
- Менять часть настроек, если это разрешено политиками.
Поэтому плохой вариант выглядит так:
aws configure set aws_secret_access_key VERY_SECRET_KEY
если команда потом остается в shell history, логах автоматизации или документации.
Еще хуже:
aws s3 sync ./backup s3://my-bucket \
--some-secret-parameter VERY_SECRET_KEYесли секрет передается прямо в командной строке.
AWS CLI как раз и позволяет хранить credentials отдельно, чтобы не повторять их в каждой команде.
Практика: устанавливаем AWS CLI и создаем профиль
На Ubuntu сначала проверим, установлен ли AWS CLI: aws --version
Если команда не найдена, можно установить пакет:
sudo apt update
sudo apt install -y awscli
После установки снова проверяем: aws --version
Теперь создадим отдельный профиль: aws configure --profile storage
AWS CLI последовательно попросит:
AWS Access Key ID:
AWS Secret Access Key:
Default region name:
Default output format:Например:
AWS Access Key ID: ACCESS_KEY
AWS Secret Access Key: SECRET_KEY
Default region name: us-east-1
Default output format: jsonEndpoint здесь пока не указываем.
В следующих командах будем передавать его явно через: --endpoint-url
Так структура подключения будет максимально прозрачной.
После настройки проверим профиль: aws configure list --profile storage
И список всех профилей: aws configure list-profiles
Команды из главы
| Задача | Команда |
| Проверить AWS CLI | aws --version |
| Установить AWS CLI | sudo apt install -y awscli |
| Создать профиль | aws configure --profile storage |
| Проверить профиль | aws configure list --profile storage |
| Посмотреть профили | aws configure list-profiles |
| Посмотреть каталог конфигурации | ls -la ~/.aws/ |
Теперь у нас есть отдельный профиль с credentials, но мы еще не доказали, что endpoint и ключи действительно работают вместе.
Проверяем подключение через собственный endpoint
Профиль уже создан, ключи сохранены, а AWS CLI знает, под каким именем использовать этот набор credentials.
Теперь пора проверить самое главное: может ли клиент действительно подключиться к S3-совместимому хранилищу конкретного провайдера.
И здесь лучше не начинать сразу с загрузки файлов, удаления объектов или создания bucket. Сначала логичнее выполнить безопасный запрос на чтение и убедиться, что endpoint, профиль и подпись работают вместе.
Почему сначала лучше выполнить безопасный read-only запрос
Если настройка еще не проверена, первая команда должна по возможности ничего не менять.
Например, вместо aws s3 mb s3://my-bucket лучше начать с обычного просмотра: aws s3 ls
Почему это полезно?
Потому что мы сразу отделяем две задачи:
- Подключение и авторизация;
- Изменение данных.
Если read-only запрос проходит, значит базовая цепочка уже работает:
- endpoint доступен;
- DNS разрешается;
- TLS-соединение устанавливается;
- Access Key распознается;
- Secret Key подходит для подписи;
- регион и формат запроса не вызывают конфликт;
- профиль действительно читается AWS CLI.
А если запрос падает, мы еще ничего не успели создать, удалить или перезаписать.
Для инфраструктурных инструментов это хороший общий принцип: сначала проверяем доступ минимально опасной операцией, потом переходим к изменениям.
Как понять, что endpoint и ключи работают
Теперь добавим собственный endpoint.
Допустим, провайдер выдал адрес:
Проверим список доступных bucket:
aws s3 ls \
--endpoint-url https://s3.example-provider.com \
--profile storageЕсли в аккаунте уже есть bucket, вывод может выглядеть примерно так:
2026-09-15 12:31:42 backups
2026-09-15 12:33:18 site-mediaЕсли bucket пока нет, команда может просто вернуть пустой результат без ошибки.
И это тоже успешное подключение.
Пустой список означает, что AWS CLI дошел до хранилища и успешно выполнил запрос, но показывать пока нечего.
Теперь можно проверить конкретный bucket:
aws s3 ls s3://my-bucket \
--endpoint-url https://s3.example-provider.com \
--profile storageЕсли внутри уже есть объекты, увидим их имена, размеры и даты.
Например:
2026-09-15 13:02:10 14321 report.pdf
2026-09-15 13:03:54 2849012 backup.sql.gzЕсли используется структура с префиксами, AWS CLI может показывать что-то похожее на каталоги: PRE backups/ и PRE images/
Но, как мы уже разбирали выше, это не обязательно реальные директории. CLI просто группирует ключи по префиксам, чтобы человеку было удобнее.
Есть еще один полезный способ проверки — использовать низкоуровневую команду S3 API:
aws s3api list-buckets \
--endpoint-url https://s3.example-provider.com \
--profile storageВ отличие от aws s3, набор s3api ближе к прямым API-операциям S3 и обычно возвращает структурированный JSON.
Например:
{
"Buckets": [
{
"Name": "backups"
}
]
}Для повседневной работы aws s3 удобнее, а s3api особенно полезен для диагностики и более тонких настроек.
Чем ошибка соединения отличается от ошибки авторизации
Вот здесь начинается действительно полезная диагностика.
Не все ошибки AWS CLI означают «неверный пароль».
Допустим, получаем: Could not connect to the endpoint URL или EndpointConnectionError
Это означает, что клиент вообще не смог нормально подключиться к endpoint.
Причины могут быть сетевыми:
- Неправильно указан URL;
- endpoint не существует;
- DNS не разрешается;
- Порт недоступен;
- firewall блокирует соединение;
- TLS-сертификат не проходит проверку;
- У сервиса временная проблема.
То есть до проверки Access Key дело может даже не дойти.
Можно проверить сам адрес отдельно:
curl -I https://s3.example-provider.com
Здесь не обязательно ожидать 200 OK: S3 endpoint может вернуть 403, 400 или XML-ошибку без подписи.
Главное — понять, отвечает ли сервер вообще.
Совсем другая ситуация: InvalidAccessKeyId
Здесь endpoint уже доступен, запрос дошел до S3-сервиса, но указанный Access Key не был принят.
Возможные причины:
- Опечатка в ключе;
- Ключ удален;
- Используется ключ от другого аккаунта;
- Ключ относится к другому S3-провайдеру;
- Выбран не тот профиль.
А SignatureDoesNotMatch означает, что сервер получил запрос и Access Key распознал, но вычисленная подпись не совпала.
Тогда проверяем уже:
- Secret Key;
- Регион;
- Системное время;
- Endpoint;
- Способ формирования host/path;
- Особенности конкретного S3-compatible сервиса.
Еще одна ошибка: AccessDenied
Она часто означает уже более интересную вещь: аутентификация прошла, но у пользователя нет права на конкретное действие.
Например, сервисному пользователю разрешено читать объекты из одного bucket, но запрещено выполнять ListAllMyBuckets.
В таком случае команда aws s3 ls может получить AccessDenied, а: aws s3 ls s3://allowed-bucket — успешно заработать.
И это важный нюанс: ошибка просмотра всех bucket не всегда означает, что credentials вообще не работают.
Права могли быть специально ограничены.
Именно поэтому при диагностике полезно знать, какие разрешения выдал провайдер или администратор.
Мини-таблица команд
| Что проверяем | Команда |
| Список доступных bucket | aws s3 ls --endpoint-url https://s3.example-provider.com --profile storage |
| Содержимое конкретного bucket | aws s3 ls s3://my-bucket --endpoint-url https://s3.example-provider.com --profile storage |
| Список bucket через API | aws s3api list-buckets --endpoint-url https://s3.example-provider.com --profile storage |
| Доступность endpoint | curl -I https://s3.example-provider.com |
| Используемый профиль | aws configure list --profile storage |
На этом этапе у нас уже есть доказательство, что профиль и endpoint работают вместе.
Теперь можно переходить от безопасных запросов к изменениям и создать первый bucket.
Создаем bucket
С понятием bucket мы уже разобрались в начале статьи, поэтому повторять его устройство здесь не будем. На этом этапе нас интересует уже практическая часть: как создать bucket через AWS CLI и какие параметры могут повлиять на результат.
На первый взгляд операция простая, но именно при создании bucket часто всплывают особенности конкретного S3-совместимого провайдера — прежде всего регион и правила уникальности имени.
Как регион влияет на создание bucket
У разных S3-совместимых сервисов работа с регионами может отличаться.
Один провайдер может использовать единственный регион для всего Object Storage, другой — несколько отдельных регионов с собственными endpoint.
Например:
Регион может участвовать не только в размещении данных, но и в формировании подписи SigV4.
Поэтому если профиль настроен с одним значением: region = us-east-1, а endpoint ожидает другое, часть операций может завершаться ошибкой.
Особенно часто это проявляется именно при создании bucket.
В некоторых реализациях S3 достаточно обычной команды:
aws s3 mb s3://my-bucket \
--endpoint-url https://s3.example-provider.com \
--profile storageА в других случаях может потребоваться явное указание региона через параметры API.
Например:
aws s3api create-bucket \
--bucket my-bucket \
--create-bucket-configuration LocationConstraint=eu-1 \
--endpoint-url https://s3.example-provider.com \
--profile storageТочное значение LocationConstraint нужно брать из документации конкретного провайдера.
То есть если aws s3 mb возвращает ошибку, не стоит сразу подозревать ключи. Возможно, endpoint и авторизация работают, а сервис просто ожидает другой региональный параметр.
Почему требования к уникальности имени зависят от провайдера
Еще один нюанс — область уникальности имени bucket.
В Amazon S3 имена bucket существуют в общем namespace соответствующего сервиса, поэтому простое имя вроде: backup, почти наверняка уже занято.
У стороннего S3-совместимого провайдера правила могут быть другими.
Имя может требоваться уникальным:
- Глобально;
- Только внутри региона;
- Внутри проекта;
- Внутри аккаунта;
- Внутри конкретного endpoint.
Поэтому безопаснее сразу выбирать более специфичные названия.
Например: company-project-prod-backups или site-media-eu-2026 вместо: data или backup
Так меньше шанс столкнуться с конфликтом, а назначение bucket будет понятно даже спустя несколько месяцев.
Есть и еще одна практическая причина не относиться к имени как к случайной подписи: оно может участвовать в URL объекта и использоваться в скриптах, политиках доступа и автоматизации.
Поэтому лучше выбрать нормальное имя сразу, чем потом строить миграцию только потому, что первый bucket назывался test123.
Практика
Допустим, используем:
Профиль: storage
Endpoint: https://s3.example-provider.com
Bucket: my-bucketСоздаем bucket:
aws s3 mb s3://my-bucket \
--endpoint-url https://s3.example-provider.com \
--profile storageПосле этого проверим список доступных bucket:
aws s3 ls \
--endpoint-url https://s3.example-provider.com \
--profile storageИ содержимое нового bucket:
aws s3 ls s3://my-bucket \
--endpoint-url https://s3.example-provider.com \
--profile storageПустой вывод здесь нормален: bucket существует, но объектов внутри пока нет.
Дополнительно можно проверить сам bucket через S3 API:
aws s3api head-bucket \
--bucket my-bucket \
--endpoint-url https://s3.example-provider.com \
--profile storageЕсли команда завершается без ошибки, bucket существует и учетная запись имеет к нему доступ.
Мини-таблица команд
| Задача | Команда |
| Создать bucket | aws s3 mb s3://my-bucket --endpoint-url https://s3.example-provider.com --profile storage |
| Посмотреть список bucket | aws s3 ls --endpoint-url https://s3.example-provider.com --profile storage |
| Посмотреть содержимое bucket | aws s3 ls s3://my-bucket --endpoint-url https://s3.example-provider.com --profile storage |
| Проверить bucket через API | aws s3api head-bucket --bucket my-bucket --endpoint-url https://s3.example-provider.com --profile storage |
Теперь хранилище уже готово принимать данные. Следующий шаг — загрузить первый объект, скачать его обратно и посмотреть, как AWS CLI работает с отдельными файлами и целыми каталогами.
Загружаем и скачиваем файлы
На этом этапе AWS CLI начинает ощущаться почти как обычная работа с файлами: есть cp, можно указывать источник и назначение, загружать отдельные объекты или целые каталоги.
Но внутри S3 логика всё же отличается от обычной файловой системы, и это особенно заметно на уровне ключей объектов.
Как object key заменяет привычный путь к файлу
Допустим, мы хотим загрузить файл: report.pdf
в bucket: my-bucket
и логически разместить его в: docs/2026/report.pdf
Для AWS CLI это будет выглядеть так: s3://my-bucket/docs/2026/report.pdf
Но важно помнить: docs/2026/report.pdf — это не путь в привычном смысле, а ключ объекта.
То есть хранилище не обязано отдельно создавать:
docs/
2026/
как реальные каталоги.
Оно просто сохраняет объект с ключом: docs/2026/report.pdf
И уже AWS CLI или веб-интерфейс провайдера может визуально показывать такие префиксы как папки.
Это особенно удобно, потому что структуру можно формировать сразу при загрузке.
Например, объект можно положить как: backups/postgresql/2026-09-15.sql.gz, даже если раньше никаких backups или postgresql не существовало.
Что происходит при загрузке объекта
Когда выполняется команда загрузки, AWS CLI читает локальный файл и отправляет его в S3 API.
Хранилище получает:
- содержимое объекта;
- bucket назначения;
- key;
- метаданные;
- параметры запроса.
После этого объект становится доступен по своему ключу.
Например, локальный файл: ./report.pdf можно загрузить как: s3://my-bucket/reports/report.pdf
При этом локальное имя и ключ вообще не обязаны совпадать.
Например: ./final-report-v7.pdf можно сохранить как: s3://my-bucket/reports/report.pdf
То есть при загрузке мы фактически задаем новое имя объекта внутри S3.
Это полезно в автоматизации, где локальный временный файл может иметь техническое имя, а в Object Storage получать нормальный постоянный key.
Что будет, если загрузить объект с тем же ключом
Здесь тоже есть важное отличие от восприятия S3 как «сетевого диска».
Если в bucket уже существует: s3://my-bucket/report.pdf и мы снова загружаем объект с тем же ключом, старый объект обычно будет заменен новым содержимым.
То есть key должен быть уникальным внутри bucket.
Если versioning у bucket не включен, предыдущую версию после перезаписи обычно уже нельзя получить обычным способом.
Если versioning поддерживается и активирован, старые версии могут сохраняться отдельно.
Поэтому команда: aws s3 cp report.pdf s3://my-bucket/report.pdf … может быть не просто «загрузкой», а фактически перезаписью уже существующего объекта.
Для backup-сценариев это особенно важно.
Например, если каждый день писать резервную копию под одним и тем же ключом: backup.sql.gz, то в bucket может оставаться только последняя версия.
Если нужно хранить историю, лучше использовать уникальные ключи:
backups/2026-09-15.sql.gz
backups/2026-09-16.sql.gz
backups/2026-09-17.sql.gz
или включать versioning, если провайдер его поддерживает.
Чем одиночная загрузка отличается от рекурсивной
Для одного файла всё просто: источник и назначение указываются явно.
Например:
file.txt
→ s3://my-bucket/file.txt
Но часто нужно перенести целый каталог.
Допустим:

Если использовать рекурсивное копирование, AWS CLI пройдет по каталогу и создаст соответствующие object key.
Например:
index.html
css/style.css
images/logo.png
могут превратиться в:
s3://my-bucket/site/index.html
s3://my-bucket/site/css/style.css
s3://my-bucket/site/images/logo.png
Важно понимать, что cp --recursive просто копирует весь выбранный набор объектов.
Он не пытается полноценно сравнивать две стороны и приводить их к одному состоянию.
Для этого существует уже другая команда — sync, которую разберем в следующей главе.
Практика
Допустим, используем:
Bucket: my-bucket
Endpoint: https://s3.example-provider.com
Profile: storageЗагрузить один файл:
aws s3 cp ./report.pdf s3://my-bucket/report.pdf \
--endpoint-url https://s3.example-provider.com \
--profile storageЗагрузить его под другим ключом:
aws s3 cp ./report.pdf s3://my-bucket/reports/2026/report.pdf \
--endpoint-url https://s3.example-provider.com \
--profile storageСкачать объект обратно:
aws s3 cp s3://my-bucket/report.pdf ./downloaded-report.pdf \
--endpoint-url https://s3.example-provider.com \
--profile storageПосмотреть содержимое bucket:
aws s3 ls s3://my-bucket \
--endpoint-url https://s3.example-provider.com \
--profile storageА для загрузки всего каталога:
aws s3 cp ./data s3://my-bucket/data/ \
--recursive \
--endpoint-url https://s3.example-provider.com \
--profile storageДалее у нас как обычно идёт удобная подборка команд.
Мини-таблица команд
| Задача | Команда |
| Загрузить файл | aws s3 cp ./file.txt s3://my-bucket/file.txt --endpoint-url ... --profile storage |
| Скачать файл | aws s3 cp s3://my-bucket/file.txt ./file.txt --endpoint-url ... --profile storage |
| Загрузить под другим ключом | aws s3 cp ./file.txt s3://my-bucket/archive/file.txt --endpoint-url ... --profile storage |
| Загрузить каталог | aws s3 cp ./data s3://my-bucket/data/ --recursive --endpoint-url ... --profile storage |
| Посмотреть содержимое | aws s3 ls s3://my-bucket --endpoint-url ... --profile storage |
Теперь мы умеем работать с отдельными файлами и целыми каталогами.
Синхронизируем каталоги через aws s3 sync
Обычная загрузка через cp хорошо подходит, когда нужно перенести конкретный файл или просто отправить весь каталог целиком.
Но если локальный каталог и bucket уже существуют и регулярно меняются, каждый раз копировать всё подряд не очень удобно.
Для таких задач в AWS CLI есть команда: aws s3 sync
Она сравнивает источник и назначение и старается передавать только те файлы, которые действительно нужно обновить.
Именно поэтому sync часто используют для:
- Резервных копий;
- Статических сайтов;
- Архивов;
- Выгрузки логов;
- Синхронизации рабочих каталогов.
Далее перейдем к сравнению.
Чем sync отличается от cp --recursive
На первый взгляд команды похожи.
Например: aws s3 cp ./data s3://my-bucket/data/ --recursive и aws s3 sync ./data s3://my-bucket/data/ — обе способны отправить целый каталог в S3.
Но логика у них разная.
cp --recursive действует ближе к обычному копированию: проходит по выбранному каталогу и копирует объекты.
sync пытается привести источник и назначение к более похожему состоянию, сравнивая содержимое и пропуская то, что считает уже актуальным.
Это особенно заметно при повторном запуске.
Допустим, у нас есть:

После первой синхронизации все три файла попали в bucket.
Затем мы изменили только: index.html
При следующем sync AWS CLI обычно не будет повторно отправлять весь каталог. Он передаст только измененный объект.
Для больших наборов данных это экономит:
- Время;
- Сетевой трафик;
- Количество операций;
- Иногда и деньги, если провайдер тарифицирует запросы или исходящий трафик.
Идём дальше.
Как AWS CLI определяет измененные файлы
Вот здесь есть важное НО.
aws s3 sync не выполняет полное побайтовое сравнение каждого локального файла с каждым объектом перед каждой операцией.
Это было бы слишком дорого.
CLI использует метаданные, в первую очередь размер и время изменения, а точное поведение зависит от направления синхронизации и используемых параметров.
Поэтому sync лучше воспринимать не как систему контроля версий и не как криптографическую проверку идентичности, а как практический механизм синхронизации.
Например, если локальный файл report.pdf не изменился по размеру и временным характеристикам, AWS CLI может считать, что повторная загрузка не нужна.
Для обычных backup- и deployment-сценариев этого чаще всего достаточно.
Но если требуется гарантированная проверка целостности, уже нужны дополнительные механизмы: checksums, versioning или отдельная логика приложения.
Есть и еще один полезный момент.
При синхронизации S3 → локальная система AWS CLI также сравнивает доступные метаданные и скачивает только то, что считает новым или измененным.
Поэтому одна и та же команда работает в обе стороны.
Что делает --delete
По умолчанию sync добавляет и обновляет нужные объекты, но не удаляет лишние данные на стороне назначения.
Представим, что локально было:

Мы синхронизировали каталог с S3.
Потом локально удалили: old.js и снова выполнили обычный: aws s3 sync ./site s3://my-bucket/site/
Объект: site/old.js в bucket может остаться.
Это сделано намеренно: обычная синхронизация не должна без дополнительного указания удалять данные.
Если же добавить: --delete
AWS CLI удалит на стороне назначения те файлы или объекты, которых больше нет в источнике.
То есть: aws s3 sync ./site s3://my-bucket/site/ --delete уже означает примерно: сделай содержимое назначения максимально похожим на источник, включая удаление лишнего.
Это очень полезно для зеркалирования.
Но одновременно и значительно опаснее.
Почему перед --delete полезен --dryrun
Флаг --delete — как раз тот случай, когда сначала лучше посмотреть, что собирается сделать команда.
Для этого есть: --dryrun
Он показывает планируемые операции, но не выполняет их.
Например:
aws s3 sync ./site s3://my-bucket/site/ \
--delete \
--dryrunAWS CLI может вывести примерно:
(dryrun) upload: ./index.html to s3://my-bucket/site/index.html
(dryrun) delete: s3://my-bucket/site/old.jsИ вот здесь можно спокойно проверить:
- Правильный ли bucket выбран;
- Правильный ли префикс указан;
- Не удалится ли что-то нужное;
- Не перепутаны ли источник и назначение.
Особенно важно это в автоматизации.
Ошибка вроде: s3://my-bucket/ вместо: s3://my-bucket/site/ в сочетании с --delete может затронуть намного больше объектов, чем ожидалось.
Поэтому для production-сценария хорошее правило: сначала sync --dryrun, потом — реальный sync.
Практика
Локальный каталог → S3:
aws s3 sync ./data s3://my-bucket/data/ \
--endpoint-url https://s3.example-provider.com \
--profile storageS3 → локальный каталог:
aws s3 sync s3://my-bucket/data/ ./data \
--endpoint-url https://s3.example-provider.com \
--profile storageПосмотреть предполагаемые изменения:
aws s3 sync ./data s3://my-bucket/data/ \
--dryrun \
--endpoint-url https://s3.example-provider.com \
--profile storageПроверить, что произойдет при удалении лишних объектов:
aws s3 sync ./data s3://my-bucket/data/ \
--delete \
--dryrun \
--endpoint-url https://s3.example-provider.com \
--profile storageИ только после проверки выполнить реальную синхронизацию:
aws s3 sync ./data s3://my-bucket/data/ \
--delete \
--endpoint-url https://s3.example-provider.com \
--profile storageДальше снова мини-таблица полезных команд.
Мини-таблица команд
| Задача | Команда |
| Локально → S3 | aws s3 sync ./data s3://my-bucket/data/ --endpoint-url ... --profile storage |
| S3 → локально | aws s3 sync s3://my-bucket/data/ ./data --endpoint-url ... --profile storage |
| Посмотреть изменения без выполнения | aws s3 sync ./data s3://my-bucket/data/ --dryrun --endpoint-url ... --profile storage |
| Проверить удаление лишних объектов | aws s3 sync ./data s3://my-bucket/data/ --delete --dryrun --endpoint-url ... --profile storage |
| Выполнить зеркальную синхронизацию | aws s3 sync ./data s3://my-bucket/data/ --delete --endpoint-url ... --profile storage |
Теперь AWS CLI умеет не просто копировать отдельные файлы, а поддерживать целые каталоги в актуальном состоянии.
Следующий логичный шаг — разобраться с несколькими профилями, потому что один и тот же AWS CLI вполне может одновременно работать с несколькими S3-совместимыми хранилищами и разными наборами ключей.
Работаем с несколькими хранилищами через профили
До этого мы использовали один профиль: storage
Для одного S3-совместимого хранилища этого достаточно.
Но в реальной работе довольно быстро появляется второй набор credentials, затем третий — и вот уже один и тот же AWS CLI должен уметь обращаться к разным провайдерам, регионам и аккаунтам.
Например:
- default
- backup-storage
- archive-storage
- production-storage
- aws
И вот здесь profiles становятся способом не перепутать, какими ключами и куда именно отправляется команда.
Зачем отдельный профиль каждому хранилищу
Представим, что у нас есть два S3-compatible сервиса.
Первый используется для резервных копий: https://s3.backup-provider.example
Второй — для медиа: https://s3.media-provider.example
У каждого:
- Свой Access Key;
- Свой Secret Key;
- Возможно, свой регион;
- Собственный endpoint;
- Отдельные права.
Если хранить всё в одном default, приходится постоянно менять настройки.
Это неудобно и опасно.
Гораздо логичнее создать: backup-storage и media-storage
Тогда команда сама по себе уже становится более понятной: aws s3 ls --profile backup-storage или aws s3 ls --profile media-storage
То есть профиль превращается в явный контекст выполнения.
Это особенно полезно при командах, которые изменяют данные.
Например, разница между: aws s3 sync ./backup s3://prod-backups/ --delete и той же командой с явно указанным: --profile backup-storage может быть весьма существенной.
Чем больше хранилищ, тем полезнее избегать неявного поведения.
Как AWS CLI выбирает credentials
AWS CLI умеет получать учетные данные из нескольких источников.
И здесь важно понимать, что они имеют приоритет.
Упрощенно можно считать, что CLI ищет credentials примерно так:
- Явно переданные параметры и окружение;
- Выбранный профиль;
- Стандартный default;
- Другие доступные механизмы credentials, если они настроены.
Для обычной работы с профилями нас особенно интересует: ~/.aws/credentials
Например:
[backup-storage]
aws_access_key_id = BACKUP_ACCESS_KEY
aws_secret_access_key = BACKUP_SECRET_KEY
[media-storage]
aws_access_key_id = MEDIA_ACCESS_KEY
aws_secret_access_key = MEDIA_SECRET_KEYА в ~/.aws/config можно хранить связанные параметры:
[profile backup-storage]
region = us-east-1
output = json
[profile media-storage]
region = eu-1
output = jsonТеперь при aws s3 ls --profile backup-storage AWS CLI понимает, какой набор credentials использовать.
Проверить, откуда именно профиль получает значения, можно так: aws configure list --profile backup-storage
Это полезно, когда внезапно кажется, что CLI использует «не те ключи».
Иногда причина оказывается вовсе не в файле credentials, а в переменной окружения, которая имеет более высокий приоритет.
Когда удобнее --profile, а когда переменные окружения
Для ручной работы --profile обычно очень удобен.
Например:
aws s3 ls \
--endpoint-url https://s3.example-provider.com \
--profile storageПреимущество очевидное: из самой команды видно, какой профиль используется.
Это особенно полезно:
- В терминале;
- При администрировании нескольких хранилищ;
- В статьях и документации;
- При ручной диагностике.
Но в автоматизации профили подходят не всегда.
Например, внутри CI/CD контейнер может вообще не иметь постоянного: ~/.aws/credentials
Тогда credentials часто передаются через переменные окружения:
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
AWS_DEFAULT_REGIONAWS CLI подхватывает их автоматически.
Это удобно для:
- CI/CD;
- Временных окружений;
- Контейнеров;
- Systemd-сервисов;
- Секретов из Vault или secret manager.
То есть выбор зависит от сценария.
Для человека: --profile обычно удобнее.
Для автоматизированного процесса — переменные окружения или специализированный secret storage могут быть безопаснее и гибче.
Но здесь есть важный нюанс: переменные окружения легко случайно сделать глобальными для сессии.
Например, если выполнить:
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
а потом забыть об этом, следующая команда может использовать эти значения даже тогда, когда вы ожидаете другой профиль.
Именно поэтому при странном поведении стоит проверить: env | grep ^AWS_
Если там остались старые credentials, они могут влиять на результат.
Почему не стоит смешивать ключи разных провайдеров
S3-compatible API выглядит одинаково, поэтому возникает ощущение, что ключи тоже взаимозаменяемы.
Но это не так.
Access Key и Secret Key создаются конкретным сервисом и действуют в пределах его системы авторизации.
Например, credentials от: Provider A не должны работать на: Provider B.
Даже если оба поддерживают aws s3 и Signature Version 4.
Если случайно использовать: endpoint Provider B, вместе с credentials Provider A, можно получить InvalidAccessKeyId или другую ошибку авторизации.
Еще хуже, если у двух профилей похожие имена и приходится вспоминать, какой из них production.
Поэтому профили лучше называть по назначению, а не:
- test1
- test2
- new
- storage2
Гораздо понятнее:
- prod-backups
- stage-backups
- media-storage
- archive-storage
И здесь работает тот же принцип, что и с именами bucket: хороший naming сегодня экономит время через полгода.
Мини-таблица команд
| Задача | Команда |
| Создать новый профиль | aws configure --profile backup-storage |
| Посмотреть все профили | aws configure list-profiles |
| Проверить конкретный профиль | aws configure list --profile backup-storage |
| Использовать профиль в S3-команде | aws s3 ls --profile backup-storage --endpoint-url ... |
| Проверить AWS-переменные окружения | env | grep ^AWS_ |
| Указать профиль на время текущей shell-сессии | export AWS_PROFILE=backup-storage |
Профили особенно полезны, когда один AWS CLI используется как универсальный клиент для нескольких S3-совместимых сервисов.
Управляем доступом к bucket и объектам
До этого момента мы в основном говорили о том, как подключиться к хранилищу и как отправлять туда команды.
Теперь пора разобраться с другим важным вопросом: Если Access Key работает, что именно он вообще имеет право делать?
И вот здесь начинается отдельный слой логики.
Потому что наличие рабочей пары: Access Key и Secret Key, еще не означает, что пользователь автоматически может:
- Читать все bucket;
- Загружать файлы куда угодно;
- Удалять объекты;
- Менять политики;
- Делать данные публичными.
Credentials подтверждают личность, а права доступа определяют допустимые действия.
Почему Access Key не означает полный доступ
Представим, что мы создали отдельного сервисного пользователя для резервных копий.
Ему нужно всего несколько возможностей:
- Посмотреть содержимое конкретного bucket;
- Загрузить новый backup;
- Скачать backup при восстановлении.
Но удалять весь bucket, менять его политику или работать с чужими хранилищами ему совершенно не обязательно.
Поэтому хорошая схема выглядит не так:
Access Key
→ полный доступ ко всему Object Storage
а скорее так:
Access Key
→ конкретный пользователь
→ конкретная policy
→ ограниченный набор действий
Именно поэтому два разных Access Key могут успешно проходить аутентификацию на одном endpoint, но иметь совершенно разные возможности.
Например, один пользователь сможет выполнить GetObject, но получит AccessDenied на: DeleteObject
Это не ошибка авторизации.
Наоборот, авторизация уже прошла правильно — просто конкретное действие запрещено.
Чем отличаются credentials, ACL и bucket policy
Эти понятия легко смешать, потому что все они связаны с доступом.
Но работают на разных уровнях.
Credentials
Credentials — это учетные данные:
- Access Key;
- Secret Key.
Они отвечают на вопрос: Кто отправляет запрос?
То есть credentials идентифицируют пользователя или сервисный аккаунт.
ACL
ACL — Access Control List.
Это более старый механизм управления доступом к bucket или отдельным объектам.
Через ACL можно, например, выдавать права определенным пользователям или делать объект публично читаемым.
Но у разных S3-compatible провайдеров поддержка ACL может быть ограничена или вообще реализована не полностью.
Некоторые современные S3-сценарии вообще предпочитают policies и отключают ACL как основной механизм управления правами.
Bucket policy
Bucket policy — это политика, которая применяется к конкретному bucket.
Она позволяет описать:
- Кто получает доступ;
- К каким ресурсам;
- Какие действия разрешены;
- При каких условиях.
По смыслу policy гораздо гибче обычного «разрешить/запретить».
Например, можно разрешить сервисному пользователю:
- Просматривать только my-backups;
- Загружать объекты;
- Читать объекты;
- Но не удалять их.
Или разрешить публичное чтение только определенного префикса.
Условно различие можно свести к таблице:
| Механизм | На какой вопрос отвечает |
| Credentials | Кто выполняет запрос |
| ACL | Какие права назначены объекту или bucket |
| Bucket policy | Кто, что и при каких условиях может делать внутри bucket |
А теперь поехали к следующей главе.
Публичный bucket и публичный object — не одно и то же
Это тоже важный момент.
Когда говорят: «bucket публичный», под этим могут подразумевать совершенно разные вещи.
Например, bucket может разрешать анонимное чтение объектов, но запрещать просмотр их полного списка.
То есть пользователь сможет открыть https://.../images/logo.png, если знает точный адрес, но не сможет запросить: покажи мне вообще всё содержимое bucket.
И наоборот, можно настроить доступ так, что определенный пользователь имеет ListBucket, но не имеет GetObject.
Это потому, что в S3: список объектов и чтение конкретного объекта — разные API-действия.
Поэтому публичность лучше не воспринимать как один переключатель «всё открыто / всё закрыто».
Особенно если речь идет о S3-compatible провайдере: интерфейс панели может упрощать настройки, а под капотом использовать собственную реализацию policies или ACL.
Принцип минимальных прав
Один из базовых принципов безопасности здесь — least privilege, или принцип минимально необходимых прав.
Сервису нужно давать только те действия, которые реально необходимы для его задачи.
Например, backup-скрипту может быть достаточно:
- ListBucket
- PutObject
- GetObject
А вот DeleteObject можно не давать вообще.
Это особенно полезно как защита от двух вещей:
- Ошибки в скрипте;
- Компрометации credentials.
Представим, что скрипт по ошибке запускается с неправильным --delete.
Если учетная запись вообще не имеет DeleteObject, хранилище просто отклонит удаление.
То есть policy становится дополнительным предохранителем.
Именно поэтому давать всем сервисным ключам полный доступ «чтобы не мешало» — удобный, но плохой подход.
Какие возможности доступа могут отличаться у S3-compatible провайдеров
Вот здесь S3-compatible снова не означает полную идентичность AWS.
Разные провайдеры могут по-разному поддерживать:
- bucket policies;
- object ACL;
- public access;
- versioning;
- Object Lock;
- lifecycle rules;
- IAM-подобных пользователей;
- отдельные service accounts;
- ограничения по IP;
- временные credentials;
- presigned URLs.
У одного провайдера можно создать сложную JSON-policy почти как в AWS.
У другого — только выбрать несколько готовых ролей в панели:
- Read Only
- Read/Write
- Full Access
А третий может разрешать управление доступом только на уровне проекта.
Поэтому перед переносом сложной политики из Amazon S3 в стороннее Object Storage лучше сначала проверить документацию конкретного сервиса.
Простые права вроде чтения и записи обычно совместимы хорошо.
А вот продвинутые механизмы IAM — уже не всегда.
Основные действия S3
Ниже — несколько прав, которые особенно часто встречаются в политиках доступа:
| Право | Что разрешает |
| ListBucket | Просматривать список объектов внутри bucket |
| GetObject | Читать и скачивать объект |
| PutObject | Загружать или перезаписывать объект |
| DeleteObject | Удалять объект |
| GetBucketLocation | Получать информацию о регионе bucket |
| ListAllMyBuckets | Просматривать список доступных bucket |
Важно, что ListBucket относится к самому bucket, а GetObject, PutObject и DeleteObject — уже к объектам внутри него.
Поэтому в policy часто приходится отдельно описывать ресурсы уровня bucket и уровня objects.
Например, логически это может выглядеть так:
Bucket:
arn:...:my-bucket
Objects:
arn:...:my-bucket/*
Точный формат зависит от конкретного провайдера и его реализации S3 policies.
На практике самый безопасный подход такой:
- Создать отдельного пользователя или service account.
- Выдать только нужный bucket.
- Разрешить только необходимые операции.
- Проверить каждое действие через AWS CLI.
- Только после этого использовать credentials в автоматизации.
Так гораздо проще понять, почему конкретная команда получает AccessDenied, и заметно безопаснее, чем выдавать полный доступ с самого начала.
Почему AWS CLI отвечает AccessDenied или SignatureDoesNotMatch
Когда AWS CLI уже установлен, профиль настроен, а endpoint известен, кажется, что дальше всё должно работать автоматически.
Но на практике именно на этом этапе чаще всего появляются ошибки вроде AccessDenied, InvalidAccessKeyId или SignatureDoesNotMatch.
Важно не воспринимать их как одну и ту же проблему. Они возникают на разных этапах обработки запроса и поэтому указывают на разные причины.
Если упростить, цепочка выглядит так: AWS CLI формирует запрос, подписывает его, отправляет на endpoint, а сервер уже проверяет credentials, подпись и права пользователя.
Неверные Access Key или Secret Key
Если указан неправильный Access Key, сервер может вообще не распознать учетную запись. В таком случае часто появляется ошибка InvalidAccessKeyId.
Причины обычно достаточно простые: опечатка, старый ключ, удаленный service account или профиль от другого S3-провайдера.
С Secret Key ситуация немного другая. Он участвует в формировании подписи, поэтому неверный Secret Key чаще приводит к SignatureDoesNotMatch.
При этом AWS CLI сам не может проверить Secret Key заранее. Он просто использует сохраненное значение для расчета подписи и отправляет результат серверу.
Поэтому если Access Key выглядит правильным, но подпись не сходится, имеет смысл повторно проверить именно Secret Key.
Отдельно стоит помнить про переменные окружения. Даже если файл ~/.aws/credentials содержит правильные значения, активные AWS_ACCESS_KEY_ID и AWS_SECRET_ACCESS_KEY могут подменить их.
Ошибка в endpoint
Неправильный endpoint может проявляться по-разному.
Если адрес вообще недоступен, AWS CLI обычно сообщает о проблеме соединения, например Could not connect to the endpoint URL.
Если же endpoint существует, но относится к другому региону или другому S3-сервису, ошибка может выглядеть уже как проблема авторизации или подписи.
Например, пользователь может случайно использовать credentials одного провайдера вместе с endpoint другого.
С точки зрения AWS CLI всё выглядит нормально: URL существует, ключи загружены, запрос подписан. Но сервер не может связать этот Access Key со своей системой и отклоняет запрос.
Поэтому endpoint всегда нужно проверять вместе с профилем, а не отдельно.
Неправильный регион
Регион участвует в SigV4, поэтому его значение влияет непосредственно на подпись.
Если сервис ожидает один регион, а CLI использует другой, сервер может получить корректно сформированный запрос, но вычислить другую подпись.
Итогом становится SignatureDoesNotMatch.
У сторонних S3-compatible провайдеров здесь бывают особенности. Некоторые используют знакомое значение вроде us-east-1 независимо от физического расположения хранилища, а другие требуют собственное имя региона.
Поэтому регион лучше брать из документации провайдера, а не определять по географическому расположению дата-центра.
Для проверки текущего значения можно использовать aws configure list --profile storage.
Рассинхронизация системного времени
В SigV4 используется время запроса.
Если часы на клиентской системе сильно спешат или отстают, сервер может посчитать запрос недействительным.
Это сделано не случайно: временная привязка помогает ограничивать повторное использование ранее подписанных запросов.
При большой разнице во времени можно встретить RequestTimeTooSkewed или другие ошибки, связанные с подписью и датой.
На VPS этот сценарий встречается редко, поскольку время обычно синхронизируется автоматически, но полностью исключать его не стоит.
Проверить состояние системных часов можно командой timedatectl. Если NTP активен, а System clock synchronized показывает yes, причина, скорее всего, находится в другом месте.
У пользователя нет нужного действия в policy
AccessDenied чаще всего означает уже не проблему credentials, а недостаток прав.
Например, пользователь может иметь GetObject, но не иметь PutObject. Тогда скачивание объекта будет работать, а загрузка завершится отказом.
То же самое относится к ListBucket, DeleteObject и другим действиям.
Есть и более интересный случай: пользователь может иметь доступ к конкретному bucket, но не иметь права просматривать список всех bucket аккаунта.
Тогда aws s3 ls вернет AccessDenied, хотя команда aws s3 ls s3://my-bucket с тем же профилем выполнится успешно.
Поэтому при диагностике важно смотреть не только на сам факт отказа, но и на конкретную операцию, которую пытался выполнить AWS CLI.
Если ошибка возникает только на одной команде, а остальные работают, проблема почти наверняка связана с policy.
Path-style и virtual-hosted-style запросы
S3 поддерживает два основных способа адресации bucket.
При path-style запросе имя bucket находится в пути:
https://s3.example-provider.com/my-bucket/object.txt
При virtual-hosted-style оно становится частью hostname:
https://my-bucket.s3.example-provider.com/object.txt
Для Amazon S3 virtual-hosted-style давно является основным вариантом.
Но у S3-compatible провайдеров поддержка может отличаться.
Например, сервис может корректно работать только с path-style или, наоборот, ожидать virtual-hosted-style.
Проблемы здесь часто выглядят странно: endpoint отвечает, credentials правильные, но отдельные запросы завершаются ошибками подписи, DNS или доступа к bucket.
Причина в том, что строка запроса и host входят в расчет подписи SigV4. Если клиент и сервер по-разному формируют адрес bucket, подпись может не совпасть.
Для AWS CLI можно принудительно использовать path-style через конфигурацию:
aws configure set s3.addressing_style path --profile storageДля возврата к автоматическому выбору используется значение auto.
Этот параметр особенно полезен со старыми или частично совместимыми S3-сервисами.
Таблица ошибок
| Ошибка | Вероятная причина | Что проверить |
InvalidAccessKeyId | Неверный или удаленный Access Key | Профиль, Access Key, аккаунт и провайдера |
SignatureDoesNotMatch | Неверный Secret Key, регион, endpoint или addressing style | Secret Key, регион, endpoint, path/virtual-hosted-style |
AccessDenied | Недостаточно прав | Policy и разрешения на конкретное действие |
RequestTimeTooSkewed | Системное время сильно отличается | timedatectl, NTP и текущую дату |
| Could not connect to the endpoint URL | Endpoint недоступен или указан неверно | URL, DNS, сеть, firewall и TLS |
NoSuchBucket | Bucket не существует или указано неправильное имя | Имя bucket, endpoint и регион |
PermanentRedirect | Используется неправильный endpoint или регион | Региональный endpoint и настройки профиля |
AuthorizationHeaderMalformed | Ошибка региона или параметров подписи | Region в профиле и требования провайдера |
Удобнее всего диагностировать такие ошибки последовательно: сначала проверить endpoint и соединение, затем профиль и credentials, потом регион и системное время, а уже после этого разбираться с policy и способом адресации bucket.
Если выполнять проверки именно в таком порядке, большая часть типичных проблем с S3-compatible хранилищами находится довольно быстро.
Теперь, когда мы знаем не только как выполнить команды, но и как искать причины ошибок, можно провести финальную проверку всей цепочки: создать или выбрать bucket, загрузить тестовый файл, скачать его обратно, выполнить sync и удалить тестовый объект.
Проверяем работу хранилища целиком
Начнем со списка доступных bucket:
aws s3 ls \
--endpoint-url https://s3.example-provider.com \
--profile storageЕсли нужный bucket уже существует, можно использовать его. Если нет — создадим тестовый:
aws s3 mb s3://my-bucket \
--endpoint-url https://s3.example-provider.com \
--profile storageТеперь подготовим небольшой локальный файл:
echo "S3 test file" > test.txt
Загрузим его в Object Storage:
aws s3 cp ./test.txt s3://my-bucket/test.txt \
--endpoint-url https://s3.example-provider.com \
--profile storageПосле загрузки проверим, что объект действительно появился:
aws s3 ls s3://my-bucket/ \
--endpoint-url https://s3.example-provider.com \
--profile storageНа этом этапе в выводе должен появиться test.txt с его размером и временем изменения.
Теперь проверим обратное направление и скачаем объект под другим локальным именем:
aws s3 cp s3://my-bucket/test.txt ./downloaded-test.txt \
--endpoint-url https://s3.example-provider.com \
--profile storageПри желании содержимое двух файлов можно сравнить:
diff test.txt downloaded-test.txt
Если команда ничего не выводит, файлы совпадают.
Следующим шагом проверим sync. Создадим небольшой каталог:
mkdir -p sync-test
echo "First file" > sync-test/first.txt
echo "Second file" > sync-test/second.txt
Синхронизируем его с отдельным префиксом внутри bucket:
aws s3 sync ./sync-test s3://my-bucket/sync-test/ \
--endpoint-url https://s3.example-provider.com \
--profile storageПосле этого содержимое можно проверить рекурсивным просмотром:
aws s3 ls s3://my-bucket/sync-test/ \
--recursive \
--endpoint-url https://s3.example-provider.com \
--profile storageЕсли оба файла отображаются, значит sync также работает корректно.
Остается проверить право на удаление. Удалим созданный ранее тестовый объект:
aws s3 rm s3://my-bucket/test.txt \
--endpoint-url https://s3.example-provider.com \
--profile storageИ повторно посмотрим содержимое:
aws s3 ls s3://my-bucket/ \
--endpoint-url https://s3.example-provider.com \
--profile storageЕсли test.txt исчез, полный цикл успешно пройден: AWS CLI может подключаться к собственному endpoint, читать содержимое bucket, загружать и скачивать объекты, синхронизировать каталоги и удалять данные.
Тестовые объекты из sync-test/ после проверки тоже можно удалить:
aws s3 rm s3://my-bucket/sync-test/ \
--recursive \
--endpoint-url https://s3.example-provider.com \
--profile storageЕсли сам bucket создавался только для этой проверки и больше не нужен, после очистки его можно удалить командой:
aws s3 rb s3://my-bucket \
--endpoint-url https://s3.example-provider.com \
--profile storageУдалять рабочий bucket ради проверки, разумеется, не требуется.
После такого теста можно считать базовую настройку AWS CLI завершенной. Осталась безопасность.
Как безопасно использовать S3 credentials в production
Почему ключи не стоит хранить в скриптах
Самый очевидный, но до сих пор распространенный анти-паттерн — прописывать Access Key и Secret Key прямо в shell-скрипте.
Например, так делать не стоит:
export AWS_ACCESS_KEY_ID="ACCESS_KEY"
export AWS_SECRET_ACCESS_KEY="SECRET_KEY"
aws s3 sync ./backup s3://my-bucket/
Технически это будет работать, но секреты окажутся внутри обычного текстового файла.
Дальше этот файл может попасть:
- в Git;
- в архив с проектом;
- в резервную копию;
- в CI-логи;
- в чужую учетную запись;
- в тикет или чат при отладке.
Еще одна проблема — shell history. Если вставлять секреты прямо в команды терминала, они могут сохраниться в истории текущего пользователя.
Поэтому production-сценарий лучше строить так, чтобы приложение или скрипт получали credentials из отдельного защищенного источника, а не содержали их внутри кода.
В зависимости от инфраструктуры это могут быть:
- ~/.aws/credentials с корректными правами доступа;
- переменные окружения, передаваемые процессу;
- secret storage CI/CD;
- Vault или другой менеджер секретов;
- встроенный механизм временных credentials, если его поддерживает конкретный провайдер.
При этом сам факт использования переменных окружения не делает систему автоматически безопасной. Важно, кто может читать окружение процесса, как секреты передаются и не попадают ли они в логи.
Ограничиваем права отдельного service account
Для production лучше не использовать один общий ключ с полным доступом ко всему Object Storage.
Намного безопаснее создать отдельный service account под конкретную задачу.
Например, backup-сервису может быть достаточно прав ListBucket, PutObject и GetObject только для одного bucket.
Если удаление резервных копий выполняется отдельной lifecycle policy, право DeleteObject такому аккаунту можно вообще не выдавать.
Это дает сразу несколько преимуществ.
Во-первых, случайная ошибка в скрипте не сможет выйти за пределы разрешенных действий.
Во-вторых, при компрометации Secret Key злоумышленник получит не полный доступ ко всему аккаунту, а только ограниченный набор возможностей.
В-третьих, становится проще понимать назначение ключей. Вместо одного универсального пользователя можно иметь отдельные учетные записи для:
- backup;
- загрузки медиа;
- CI/CD;
- архивирования;
- чтения отчетов.
Для каждого service account лучше задавать отдельные credentials и минимально необходимые права.
Когда стоит менять ключи
Secret Key не обязательно менять каждый понедельник только ради самого факта ротации.
Гораздо важнее уметь быстро заменить его тогда, когда действительно появляется риск.
Ключ нужно немедленно отозвать и создать новый, если он:
- Попал в Git;
- Был отправлен в чат или тикет;
- Оказался в логах;
- Хранился на скомпрометированном сервере;
- Стал доступен сотруднику или подрядчику, которому больше не нужен доступ.
Периодическая ротация тоже полезна, особенно для долгоживущих production credentials.
Хороший процесс ротации выглядит так: сначала создается новый ключ, приложения переводятся на него, подключение проверяется, и только после этого старый ключ отключается.
Так меньше риск случайно остановить backup или другой сервис из-за преждевременного удаления credentials.
Если провайдер поддерживает несколько активных ключей для одного service account, такая схема особенно удобна.
Почему Object Storage сам по себе не отменяет backup
Еще одна типичная ошибка — считать, что файл уже стал резервной копией просто потому, что он лежит в Object Storage.
Object Storage действительно повышает надежность хранения, но оно не защищает от всех сценариев потери данных.
Например, если скрипт ошибочно удаляет объект и у его credentials есть DeleteObject, удаление может успешно синхронизироваться с удаленным хранилищем.
То же самое относится к:
- Ошибочной перезаписи;
- Поврежденным исходным данным;
- Компрометации credentials;
- Ошибке администратора;
- Неправильному sync --delete.
Поэтому надежная схема обычно использует дополнительные механизмы.
Если провайдер поддерживает versioning, предыдущие версии объектов можно сохранять после перезаписи.
Lifecycle rules позволяют автоматически переводить старые версии в другой класс хранения или удалять их через заданный срок.
Object Lock может защищать данные от изменения и удаления в течение определенного периода.
А для особенно важных данных разумно иметь независимую копию в другом аккаунте, регионе или даже у другого провайдера.
То есть Object Storage — отличная платформа для резервного копирования, но не самостоятельная гарантия того, что backup невозможно потерять.
Полезно разделять два понятия:
- Удаленная копия данных;
- Полноценная стратегия резервного копирования.
Вторая обычно включает историю версий, сроки хранения, контроль доступа и регулярную проверку восстановления.
Заключение

S3-совместимое Object Storage позволяет работать с хранилищами разных провайдеров через привычный S3 API и один и тот же AWS CLI.
Для подключения достаточно знать endpoint, регион и credentials, но за этой простой схемой скрывается несколько важных деталей: подпись SigV4, profiles, права доступа, addressing style и особенности реализации S3 у конкретного сервиса.
В статье мы прошли весь основной рабочий цикл: настроили AWS CLI, создали отдельный профиль, подключились к собственному endpoint, создали bucket, загрузили и скачали объекты, синхронизировали каталоги и разобрали типовые ошибки авторизации.
Для production остается соблюдать несколько простых правил: не хранить Secret Key в коде, использовать отдельные service accounts, выдавать минимальные права и заранее продумать замену credentials.
А если Object Storage используется для резервных копий, стоит отдельно настроить versioning, retention или другую защиту от случайной перезаписи и удаления.
После этого AWS CLI становится не просто удобной утилитой для ручной загрузки файлов, а полноценным инструментом для backup, автоматизации, CI/CD и работы с большими наборами объектов.
FAQ
Можно ли использовать AWS CLI с S3-хранилищем, которое не принадлежит Amazon?
Да. Если провайдер поддерживает совместимый S3 API, AWS CLI можно направить на его собственный endpoint через --endpoint-url. При этом конкретные возможности — policies, ACL, versioning, Object Lock и другие функции — могут отличаться от Amazon S3.
Почему aws s3 ls возвращает AccessDenied, а доступ к конкретному bucket работает?
Команда просмотра всех bucket требует отдельного разрешения. Пользователь может иметь доступ к конкретному bucket и его объектам, но не иметь права получать общий список хранилищ аккаунта.
Поэтому AccessDenied в таком случае не означает, что credentials настроены неправильно.
Что произойдет, если повторно загрузить файл с тем же object key?
Новый объект заменит существующий под тем же ключом. Если versioning не включен, прежняя версия обычно перестает быть доступной. Для резервных копий поэтому полезно использовать уникальные ключи или включать versioning, если провайдер его поддерживает.
Можно ли один профиль AWS CLI использовать для нескольких S3-провайдеров?
Технически можно постоянно менять endpoint и credentials, но это быстро приводит к путанице. Надежнее создать отдельный профиль для каждого провайдера или назначения: например, prod-backups, archive-storage и media-storage.
Почему sync --delete считается опасной командой?
Параметр --delete удаляет на стороне назначения объекты, которых нет в источнике. Если перепутать bucket, префикс или направление синхронизации, можно удалить нужные данные.
Перед такой операцией желательно сначала выполнить ту же команду с --dryrun и проверить запланированные изменения.
В чем разница между aws s3 и aws s3api?
aws s3 предоставляет высокоуровневые команды вроде cp, sync, ls и rm, удобные для повседневной работы с файлами.
aws s3api ближе к отдельным операциям S3 API и предоставляет более точный контроль. Он полезен, например, для работы с настройками bucket, policies и других функций, которые не представлены высокоуровневыми командами.
Нужно ли делать отдельные резервные копии данных, если они уже находятся в Object Storage?
Да, если данные действительно важны. Само по себе Object Storage не защищает от удаления с действительными credentials, ошибочной перезаписи или некорректного sync --delete.
Для критичных данных стоит использовать versioning, Object Lock или независимую резервную копию — в зависимости от возможностей конкретного провайдера.


