parser

Простой сервер Amazon S3

Автор: MoKo [19 августа 2026]
Версия:
Тэги: S3

Реализована лишь часть API S3 — одиночная и multipart загрузка объектов, GET/HEAD/DELETE и листинг бакета в духе ListObjectsV2 — этого достаточно, чтобы сервер мог работать как backend для настоящего S3-клиента вроде rclone.

Подключение

Чтобы подключить скрипт, направьте CGI/mod_parser обработчик Apache на s3-server.p и перепишите на него всё, что не является уже существующим статическим файлом:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule .* - [L]
RewriteRule .* /s3-server.p [L,QSA]

Первое правило отдаёт напрямую любой файл, который уже существует на диске. Всё остальное — запись, multipart-операции, листинг бакета и трюк с редиректом для GET/HEAD по отдельным ключам — попадает в скрипт.

S3-клиенты передают access key id в заголовке Authorization, но Apache по умолчанию вырезает этот заголовок из CGI-запросов. Чтобы скрипт увидел его как $env:HTTP_AUTHORIZATION, нужна директива:

CGIPassAuth On

Она должна действовать именно в контексте директории, откуда реально запускается CGI-скрипт (обычно cgi-bin), а не там, где лежат приведённые выше RewriteRule — пропишите её прямо в конфиге Apache для этой директории, либо в cgi-bin/.htaccess, если там разрешён AllowOverride.

Без неё ^auth[] никогда не увидит значение Credential=... — любой бакет, у которого в $ALLOW задан $.accessKeyId, будет отвергать запросы с 403. Это не влияет на бакеты, где проверяется только IP.

Доступ и добавление бакетов

Отдельного API для создания бакета нет — PUT, которым клиенты обычно создают бакет, здесь просто ничего не делает. А вот в какие бакеты клиенту вообще разрешено писать — отдельный вопрос, который решает хэш $ALLOW в @auto[], с именами бакетов в качестве ключей:

$ALLOW[
	$.default[
		$.ip[^^127\.0\.0\.1]
		$.accessKeyId[]
	]
]

Чтобы добавить новый бакет, добавьте ещё одну запись в $ALLOW с именем бакета в качестве ключа:

$ALLOW[
	$.default[
		$.ip[^^127\.0\.0\.1]
		$.accessKeyId[]
	]
	$.my-bucket[
		$.ip[^^10\.0\.0\.]
		$.accessKeyId[some-secret-id]
	]
]

У каждой записи есть две независимые и необязательные проверки:

  • $.ip — регулярное выражение, сверяемое с $env:REMOTE_ADDR. Не задано — подходит любой адрес.
  • $.accessKeyId — сверяется с access key id, извлечённым из заголовка Authorization запроса (часть Credential=...). Не задано — подходит любой ключ.

Запросы к бакету, которого нет в $ALLOW, отклоняются сразу. Обратите внимание: это намеренно не настоящая проверка подписи AWS SigV4 — нет проверки, что запрос действительно подписан соответствующим секретным ключом. Этого достаточно для того, чтобы ограничить доступ к бакету известным IP и/или общим access key id, работающим как bearer-токен; относитесь к $.accessKeyId как к общему секрету, а не как к настоящей аутентификации запроса.

Структура хранения

  • $PREFIX (/storage) — где лежат готовые объекты, по подкаталогу на бакет; ключ объекта напрямую отображается в путь на диске.
  • $MULTIPART (/multipart) — временная область для незавершённых multipart-загрузок. Части пишутся в bucket/key/uploadId/partNumber и собираются в итоговый объект только когда клиент присылает CompleteMultipartUpload; прерванная или брошенная загрузка просто оставляет там временные файлы.
  • Лог запросов по желанию можно писать в файл.

Поддерживаемые операции

  • Создание бакета — PUT без ключа; директория бакета появляется на диске при первой записи объекта.
  • Листинг объектов — GET без ключа: в духе ListObjectsV2, поддерживает prefix, delimiter, max-keys и пагинацию через continuation-token/marker.
  • Загрузка объекта — PUT: загрузка; в ответе выставляется ETag по md5 тела запроса.
  • Получение объекта / HEAD — GET / HEAD: отдаётся через CGI Local Redirect, т.е. файл отдаёт напрямую Apache, а не Parser.
  • Удаление объекта — DELETE.
  • Инициация multipart-загрузки — POST без uploadId.
  • Загрузка части — PUT с partNumber и uploadId.
  • Завершение multipart-загрузки — POST с uploadId: части собираются по возрастанию номера части.
  • Отмена multipart-загрузки — DELETE с uploadId: удаляет все загруженные части.
Скачать:

s3-server.zip (20.08.2026  2,9 КБ)
Простой S3-совместимый сервер.