Skip to content

forepath/laravel-s3-server

Repository files navigation

Laravel S3 Server

Latest Version on Packagist Tests Total Downloads License

A lightweight, Laravel-compatible Amazon S3 protocol server that allows you to run your own S3-compatible storage service within your Laravel application. Perfect for development, testing, or self-hosted storage solutions.

Features

  • 🔐 Full S3 Protocol Support - Compatible with AWS S3 API v4
  • 🔐 Secure Authentication - AWS Signature Version 4 (AWS4-HMAC-SHA256) authentication
  • 💾 Database-Backed Credentials - Store and manage S3 credentials in your database
  • 🔒 Bucket-Level Access Control - Restrict credentials to specific buckets for enhanced security
  • 🔒 Encrypted Storage - Secret keys are automatically encrypted using Laravel's encryption
  • 🔒 Flexible Storage Drivers - File-based storage with extensible driver system
  • 🛡️ Laravel Integration - Seamless integration with Laravel's service container
  • Lightweight - Minimal overhead, maximum performance
  • 🔒 Production Ready - Built with security and reliability in mind

Requirements

  • PHP 8.1 or higher
  • Laravel 10, 11, or 12
  • Database (MySQL, PostgreSQL, SQLite, etc.)

Installation

You can install the package via Composer:

composer require forepath/laravel-s3-server

Quick Start

1. Publish Configuration

Publish the configuration file to customize the S3 server settings:

php artisan vendor:publish --provider="LaravelS3Server\S3ServiceProvider" --tag="s3server-config"

2. Run Migrations

Run the migrations to create the necessary database tables:

php artisan migrate

This will create the s3_access_credentials table with support for bucket restrictions.

3. Create S3 Credentials

Add S3 credentials to your database:

use LaravelS3Server\Models\S3AccessCredential;

S3AccessCredential::create([
    'access_key_id' => 'AKIAIOSFODNN7EXAMPLE',
    'secret_access_key' => 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
    'description' => 'Development credentials',
    'bucket' => null, // Optional: restrict to specific bucket
]);

4. Start Using S3

Your S3 server is now available at /s3/{bucket}/{key} and supports all standard S3 operations!

Configuration

The configuration file is located at config/s3server.php:

return [
    // Enable/disable authentication
    'auth' => true,
    
    // Authentication driver class
    'auth_driver' => LaravelS3Server\Drivers\DatabaseAuthenticationDriver::class,
    
    // Storage driver class
    'storage_driver' => LaravelS3Server\Drivers\FileStorageDriver::class,
];

Authentication Settings

  • auth: Set to false to disable authentication (not recommended for production)
  • auth_driver: The authentication driver class to use

Storage Settings

  • storage_driver: The storage driver class to use for file operations

Usage

S3 API Endpoints

The package automatically registers the following S3-compatible endpoints:

Method Endpoint Description
PUT /s3/{bucket}/{key} Upload an object
GET /s3/{bucket}/{key} Download an object
GET /s3/{bucket} List bucket contents
DELETE /s3/{bucket}/{key} Delete an object

Using with AWS CLI

Configure AWS CLI to use your local S3 server:

aws configure set aws_access_key_id AKIAIOSFODNN7EXAMPLE
aws configure set aws_secret_access_key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
aws configure set region us-east-1
aws configure set endpoint_url http://localhost:8000/s3

Using with AWS SDK

use Aws\S3\S3Client;

$s3Client = new S3Client([
    'version' => 'latest',
    'region'  => 'us-east-1',
    'endpoint' => 'http://localhost:8000/s3',
    'use_path_style_endpoint' => true,
    'credentials' => [
        'key'    => 'AKIAIOSFODNN7EXAMPLE',
        'secret' => 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
    ],
]);

// Upload a file
$s3Client->putObject([
    'Bucket' => 'my-bucket',
    'Key'    => 'example.txt',
    'Body'   => 'Hello, World!',
]);

// Download a file
$result = $s3Client->getObject([
    'Bucket' => 'my-bucket',
    'Key'    => 'example.txt',
]);

echo $result['Body'];

Managing Credentials

Creating Credentials

use LaravelS3Server\Models\S3AccessCredential;

// Create a new credential with access to any bucket
$credential = S3AccessCredential::create([
    'access_key_id' => 'AKIAIOSFODNN7EXAMPLE',
    'secret_access_key' => 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
    'description' => 'Development credentials',
    'bucket' => null, // null means access to any bucket
]);

// Create a credential restricted to a specific bucket
$restrictedCredential = S3AccessCredential::create([
    'access_key_id' => 'AKIAIOSFODNN7EXAMPLE2',
    'secret_access_key' => 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY2',
    'description' => 'Production credentials for user-uploads bucket',
    'bucket' => 'user-uploads', // Only access to 'user-uploads' bucket
]);

Bucket Restrictions

The S3 server supports bucket-level access control. You can restrict credentials to access only specific buckets:

  • No Restriction: Set bucket to null to allow access to any bucket
  • Single Bucket: Set bucket to a specific bucket name to restrict access to only that bucket
  • Security: Credentials with bucket restrictions will receive 401 Unauthorized when trying to access other buckets

Listing Credentials

// Get all credentials
$credentials = S3AccessCredential::all();

// Find by access key ID
$credential = S3AccessCredential::where('access_key_id', 'AKIAIOSFODNN7EXAMPLE')->first();

Updating Credentials

$credential = S3AccessCredential::where('access_key_id', 'AKIAIOSFODNN7EXAMPLE')->first();
$credential->update([
    'description' => 'Updated description',
]);

Deleting Credentials

$credential = S3AccessCredential::where('access_key_id', 'AKIAIOSFODNN7EXAMPLE')->first();
$credential->delete();

Architecture

Authentication Drivers

The package uses a driver-based authentication system. The default DatabaseAuthenticationDriver authenticates requests using credentials stored in the database.

Custom Authentication Driver

Create a custom authentication driver by implementing the AuthenticationDriver:

<?php

namespace App\Services;

use Illuminate\Http\Request;
use LaravelS3Server\Contracts\AuthenticationDriver;

class CustomAuthDriver implements AuthenticationDriver
{
    public function authenticate(Request $request): bool
    {
        // Your custom authentication logic
        return true;
    }

    public function getSecretKeyByAccessKeyId(string $accessKeyId): ?string
    {
        // Return the secret key for the given access key ID
        return 'your-secret-key';
    }
}

Then update your configuration:

'auth_driver' => App\Services\CustomAuthDriver::class,

Storage Drivers

The package uses a driver-based storage system. The default FileStorageDriver stores files using Laravel's Storage facade.

Custom Storage Driver

Create a custom storage driver by implementing the S3StorageDriver interface:

<?php

namespace App\Services;

use LaravelS3Server\Contracts\S3StorageDriver;

class CustomStorageDriver implements S3StorageDriver
{
    public function put(string $path, string $content): void
    {
        // Store the file
    }

    public function get(string $path): ?string
    {
        // Retrieve the file content
        return null;
    }

    public function delete(string $path): void
    {
        // Delete the file
    }

    public function list(string $prefix): array
    {
        // List files in the prefix
        return [];
    }
}

Then update your configuration:

'storage_driver' => App\Services\CustomStorageDriver::class,

Security

Credential Encryption

All secret access keys are automatically encrypted using Laravel's built-in encryption system. The encryption uses your application's APP_KEY and AES-256-CBC encryption.

Authentication

The package implements AWS Signature Version 4 (AWS4-HMAC-SHA256) authentication, which is the same authentication method used by AWS S3. This ensures compatibility with all S3 clients and SDKs.

Best Practices

  1. Use HTTPS in Production: Always use HTTPS when deploying to production
  2. Rotate Credentials: Regularly rotate your S3 credentials
  3. Monitor Access: Implement logging to monitor S3 access
  4. Backup APP_KEY: Ensure your APP_KEY is backed up securely
  5. Limit Permissions: Use the principle of least privilege

Testing

Manual Testing

You can test the S3 server using the AWS CLI:

# List buckets (will show bucket-like directories)
aws s3 ls s3:// --endpoint-url http://localhost:8000/s3

# Create a bucket (upload a file to create the bucket)
aws s3 cp test.txt s3://my-bucket/test.txt --endpoint-url http://localhost:8000/s3

# List bucket contents
aws s3 ls s3://my-bucket/ --endpoint-url http://localhost:8000/s3

# Download a file
aws s3 cp s3://my-bucket/test.txt downloaded.txt --endpoint-url http://localhost:8000/s3

# Delete a file
aws s3 rm s3://my-bucket/test.txt --endpoint-url http://localhost:8000/s3

Programmatic Testing

use LaravelS3Server\Models\S3AccessCredential;

// Create test credentials with bucket restriction
S3AccessCredential::create([
    'access_key_id' => 'test-key',
    'secret_access_key' => 'test-secret',
    'description' => 'Test credentials for test-bucket',
    'bucket' => 'test-bucket',
]);

// Test with AWS SDK
$s3Client = new S3Client([
    'version' => 'latest',
    'region'  => 'us-east-1',
    'endpoint' => 'http://localhost:8000/s3',
    'use_path_style_endpoint' => true,
    'credentials' => [
        'key'    => 'test-key',
        'secret' => 'test-secret',
    ],
]);

// Test upload
$s3Client->putObject([
    'Bucket' => 'test-bucket',
    'Key'    => 'test.txt',
    'Body'   => 'Test content',
]);

Troubleshooting

Common Issues

1. Authentication Failures

Problem: Getting 401 Unauthorized errors

Solutions:

  • Verify credentials exist in the database
  • Check that the access key ID matches exactly
  • Ensure the request includes proper AWS Signature Version 4 headers
  • Verify your APP_KEY is set correctly

2. File Storage Issues

Problem: Files not being stored or retrieved

Solutions:

  • Check Laravel's storage configuration
  • Verify storage directory permissions
  • Ensure the storage driver is properly configured

3. Configuration Issues

Problem: Configuration not being loaded

Solutions:

  • Publish the configuration file: php artisan vendor:publish --provider="LaravelS3Server\S3ServiceProvider" --tag="s3server-config"
  • Clear configuration cache: php artisan config:clear
  • Verify the service provider is registered

4. Migration Issues

Problem: Database tables not created

Solutions:

  • Run migrations: php artisan migrate
  • Check database connection
  • Verify migration files are present

Debug Mode

Enable debug mode to get more detailed error information:

// In config/s3server.php
'debug' => true,

Contributing

We welcome contributions! Please see our Contributing Guide for details.

Development Setup

  1. Fork the repository
  2. Clone your fork
  3. Install dependencies: composer install
  4. Run tests: composer test
  5. Make your changes
  6. Submit a pull request

Code Style

This package follows Laravel's coding standards. We use Laravel Pint for code formatting:

./vendor/bin/pint

Changelog

Please see CHANGELOG.md for more information on what has changed recently.

Credits

License

The MIT License (MIT). Please see License File for more information.

Support


Built with ❤️ for the Laravel community