Introduction
MinIO is a powerful, S3-compatible object storage server. While MinIO provides built-in encryption and identity management, exposing it directly to the internet is not always the safest approach for production environments.
This tutorial covers a comprehensive hardening strategy for a MinIO standalone deployment. We will use Nginx as a reverse proxy to handle SSL termination and separate the traffic between the S3 API and the Web Console. Furthermore, we will add an extra security layer to the Web Console using Nginx Basic Auth and configure Fail2Ban to automatically ban IP addresses that repeatedly fail this authentication.
Prerequisites
- A server running Debian 12 - 13 OR Ubuntu 20.04 - 24.04.
- Root or sudo access to the server.
- MinIO installed and running (Service active).
See this tutorial
- Two subdomains pointing to your server's IP:
cdn.example.comwebui.example.com
Step 1 - Install Necessary Packages
First, update your package list and install Nginx, Certbot (for SSL), and apache2-utils (used to generate password files for Nginx).
sudo apt update
sudo apt install nginx certbot python3-certbot-nginx apache2-utils fail2ban -yEnsure the services are running:
sudo systemctl start nginx
sudo systemctl enable nginx
sudo systemctl start fail2ban
sudo systemctl enable fail2banStep 2 - Configure MinIO for Reverse Proxy
By default, MinIO might listen on all interfaces. To secure it properly behind Nginx, it is best practice to ensure MinIO listens on specific ports for the API and the Console.
Edit your MinIO configuration file (usually found at /etc/default/minio):
sudo nano /etc/default/minioAdd or modify the following lines to ensure the Console runs on a dedicated port (e.g., 9001) and the API on 9000:
# MinIO Listening Address
MINIO_ADDRESS=":9000"
MINIO_CONSOLE_ADDRESS=":9001"
# Define the Console URL (required for correct redirects behind a proxy)
MINIO_BROWSER_REDIRECT_URL="https://webui.example.com/"Restart MinIO to apply changes:
sudo systemctl restart minioStep 3 - Generate SSL Certificates
We will generate SSL certificates for both subdomains using Let's Encrypt.
sudo certbot certonly --nginx -d cdn.example.com -d webui.example.comFollow the on-screen instructions. Once finished, your certificates will be stored in /etc/letsencrypt/live/.
Step 4 - Create Nginx Basic Auth
We want to protect the cdn.example.com/webui interface with a double authentication mechanism. Before seeing the MinIO login screen, the user must pass an Nginx Basic Auth check.
Create a hidden file to store the credentials:
sudo htpasswd -c /etc/nginx/.minio_htpasswd myadminYou will be prompted to enter and confirm a password. Keep this password safe.
Step 5 - Configure Nginx
Now we will create the Nginx server block :
sudo nano /etc/nginx/sites-available/cdn.example.comPaste the following configuration (replace server_name with your actual domain and ssl_certificate + ssl_certificate_key with the correct path):
server {
listen 80;
server_name cdn.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name cdn.example.com;
ssl_certificate /etc/letsencrypt/live/cdn.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/cdn.example.com/privkey.pem;
ignore_invalid_headers off;
client_max_body_size 0;
proxy_buffering off;
proxy_request_buffering off;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 300;
proxy_http_version 1.1;
chunked_transfer_encoding off;
proxy_pass http://localhost:9000;
}
}Do the same for the WebUI
sudo nano /etc/nginx/sites-available/webui.example.comPaste the following configuration (replace server_name with your actual domain and ssl_certificate + ssl_certificate_key with the correct path):
server {
listen 80;
server_name webui.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name webui.example.com;
ssl_certificate /etc/letsencrypt/live/webui.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/webui.example.com/privkey.pem;
ignore_invalid_headers off;
client_max_body_size 0;
proxy_buffering off;
proxy_request_buffering off;
location ~ \.(css|js|map|json|svg)$ {
auth_basic off;
proxy_pass http://localhost:9001;
}
location ~ ^/(minio/)?api/v1/ {
auth_basic off;
proxy_pass http://localhost:9001;
}
location / {
# --- Web Auth ---
auth_basic "Restricted Admin Area";
auth_basic_user_file /etc/nginx/.minio_htpasswd;
proxy_pass http://localhost:9001;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-NginX-Proxy true;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}Enable the site and test the configuration:
sudo ln -s /etc/nginx/sites-available/cdn.example.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/webui.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxStep 6 - Configure Fail2Ban
Now that Nginx requests Basic Auth, we can track failed attempts in the Nginx error logs and ban the offending IPs using Fail2Ban.
-
Configure the Jail
Create a new jail configuration file:
sudo nano /etc/fail2ban/jail.d/nginx-auth.confAdd the following content:
[nginx-http-auth] enabled = true port = http,https logpath = /var/log/nginx/error.log maxretry = 3 bantime = 3600 findtime = 600Description maxretry = 3 Ban after 3 failed attempts. bantime = 3600 Ban the IP for 1 hour (3600 seconds).
-
Verify the Filter
Fail2Ban comes with a pre-configured filter for Nginx Auth. You can verify it exists at
/etc/fail2ban/filter.d/nginx-http-auth.conf. It typically looks for "user ... was not found in" or "password mismatch" in the logs.Restart Fail2Ban to apply the new jail:
sudo systemctl restart fail2banVerify the jail is correctly enabled:
sudo fail2ban-client statusYou should see :
Status |- Number of jail: 1 `- Jail list: nginx-http-auth
Conclusion
Your MinIO installation is now significantly hardened. When you connect to webui.example.com, you have to provide the credentials you set in step 4 (e.g. myadmin and your custom password).