I’ve been playing around quite a bit with MinIO lately – after all, why use cloud services where everything is readily available if I can complicate things a bit by doing everything manually?

Anyway… I came across a situation here and had it noted down in my Obsidian, but I thought it would be interesting to put it here on the blog (which I’m not sure if it still makes sense to maintain, to be honest).

The idea:

  • I have several projects and want to keep bucket access separated by project, so that one doesn’t impact the other.
  • I want each project to have its own access user, keeping them isolated.
  • Yes, I could do it the easy way, creating just folders for each project and be happy, but who said happiness is an option when we work in IT? 🙂

Now, why?

Advantages of This Approach

  • Isolation: Each project has its own storage space
  • Security: Each application only has access to its own bucket
  • Operational efficiency: A single MinIO instance for all projects
  • Flexibility: Easy to add new projects without reconfiguring the entire infrastructure

Alright, let’s go!

Prerequisites

  • Docker installed (for MinIO execution)
  • Windows PowerShell

1. MinIO Configuration

1.1 Check if MinIO is already running

# Check if MinIO is running
docker ps | findstr minio

1.2 If MinIO is not running

If the command above doesn’t return any results, you need to create and start the MinIO container:

# Create a directory for data persistence
mkdir -p d:\Projetos\minio\data

# Run the MinIO container
docker run -d \
  --name minio \
  -p 9000:9000 \
  -p 9001:9001 \
  -e "MINIO_ROOT_USER=minioadmin" \
  -e "MINIO_ROOT_PASSWORD=minioadmin" \
  -v d:\Projetos\minio\data:/data \
  minio/minio server /data --console-address ":9001"

What we’re doing with this:

  • Creating a container called “minio”
  • Exposing the MinIO API on port 9000
  • Exposing the web administration interface on port 9001
  • Setting the default user and password as “minioadmin”
  • Mounting the local directory for data persistence

To access the MinIO administration console, just use the URL: http://localhost:9001

2. MinIO Client (mc) Installation

# Download the MinIO client for Windows
Invoke-WebRequest -Uri https://dl.min.io/client/mc/release/windows-amd64/mc.exe -OutFile mc.exe

# Configure the client to connect to the local MinIO instance
.\mc.exe alias set local http://localhost:9000 minioadmin minioadmin

3. Creating Buckets

# Create buckets for each project
.\mc.exe mb local/project-a
.\mc.exe mb local/project-b

4. Creating Access Policies

For greater references about the JSON, go to the end of the page.

Create policy file for Project A (policy-project-a.json):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:GetBucketLocation"
      ],
      "Resource": [
        "arn:aws:s3:::project-a"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource": [
        "arn:aws:s3:::project-a/*"
      ]
    }
  ]
}

Create policy file for Project B (policy-project-b.json):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:GetBucketLocation"
      ],
      "Resource": [
        "arn:aws:s3:::project-b"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource": [
        "arn:aws:s3:::project-b/*"
      ]
    }
  ]
}
# Create policies from JSON files
.\mc.exe admin policy create local policy-project-a .\policy-project-a.json
.\mc.exe admin policy create local policy-project-b .\policy-project-b.json

# Associate policies with users
.\mc.exe admin user add local user-project-a password123
.\mc.exe admin user add local user-project-b password456

# Apply policies to users
.\mc.exe admin policy attach local policy-project-a --user user-project-a
.\mc.exe admin policy attach local policy-project-b --user user-project-b

5. Testing Access

5.1 Testing with MinIO Client

# Configure alias for Project A user
.\mc.exe alias set project-a-alias http://localhost:9000 user-project-a password123

# Test access to Project A bucket
.\mc.exe ls project-a-alias/project-a

# Try to access Project B bucket (should fail)
.\mc.exe ls project-a-alias/project-b

5.2 Testing with Python (boto3)

import boto3
from botocore.exceptions import ClientError

# Configure S3 client for Project A
s3_client = boto3.client(
    's3',
    endpoint_url='http://localhost:9000',
    aws_access_key_id='user-project-a',
    aws_secret_access_key='password123',
    region_name='us-east-1'
)

# List objects in bucket
response = s3_client.list_objects_v2(Bucket='project-a')

6. Adding New Projects

To add a new project, follow these steps:

  1. Create a new bucket:

    .\mc.exe mb local/project-name
    
  2. Create a policy file for the new project (policy-project-name.json)

  3. Create the policy in MinIO:

    .\mc.exe admin policy create local policy-project-name .\policy-project-name.json
    
  4. Create a user for the project:

    .\mc.exe admin user add local user-project-name secure-password
    
  5. Associate the policy with the user:

    .\mc.exe admin policy attach local policy-project-name --user user-project-name
    

JSON Policy Reference

Basic Structure

{
   "Version": "2012-10-17",
   "Statement": [
      {
         "Effect": "Allow",
         "Action": ["s3:<ActionName>", ...],
         "Resource": "arn:aws:s3:::*",
         "Condition": { ... }
      }
   ]
}

Valid Values for Each Field

1. Version

  • Fixed value: "2012-10-17" (current specification standard)

2. Effect

  • Valid values: "Allow" or "Deny"

3. Action

Common S3 actions supported by MinIO:

  • "s3:*" – All actions
  • "s3:GetObject" – Get objects
  • "s3:PutObject" – Put objects
  • "s3:DeleteObject" – Delete objects
  • "s3:ListBucket" – List bucket contents
  • "s3:GetBucketLocation" – Get bucket location
  • "s3:CreateBucket" – Create bucket
  • "s3:DeleteBucket" – Delete bucket

4. Resource

The format follows the ARN (Amazon Resource Name) standard:

  • "arn:aws:s3:::*" – All buckets and objects
  • "arn:aws:s3:::bucket-name" – A specific bucket
  • "arn:aws:s3:::bucket-name/*" – All objects in a specific bucket
  • "arn:aws:s3:::bucket-name/prefix/*" – Objects with a specific prefix

Supports wildcard characters:

  • * – Matches zero or more characters
  • ? – Matches exactly one character

5. Principal

  • {"AWS": ["*"]} – Any user (anonymous access)
  • {"AWS": ["arn:aws:iam::account-id:user/username"]} – Specific user

6. Condition (Not used here)

Allows defining additional conditions for policy application, such as:

  • IP-based conditions
  • Tag-based conditions
  • Date/time-based conditions

Additional References

Useful Commands

  • List all buckets: .\mc.exe ls local
  • List objects in a bucket: .\mc.exe ls local/bucket-name
  • Upload a file: .\mc.exe cp ./file.txt local/bucket-name/
  • Download a file: .\mc.exe cp local/bucket-name/file.txt ./
  • Remove a file: .\mc.exe rm local/bucket-name/file.txt