Securing MinIO URL using NGINX

Securing MinIO URL using NGINX

When deploying objects to the MinIO server, one of the potential security concerns is the direct accessibility of the MinIO URL. Generally, we use pre-signed URLs in our application that grant temporary access to private objects. While these URLs have an expiration time and provide a level of security, they can still be accessed by anyone who possesses the link within the specified time window.

This can become a security risk if unauthorized users gain access to these pre-signed URLs then they can misuse or expose the sensitive data stored in the MinIO bucket. Therefore, there is a need to implement an additional layer of security to prevent unauthorized access to the URLs, even within the expiration time.

In this post, I will provide step-by-step instructions on how to set up NGINX as a reverse proxy for MinIO and demonstrate how this configuration enhances the security of the MinIO setup.

1. Setup a MinIO Server

If you have a problem setting up MinIO server visit MinIO's Official documentation.

Step 1.1) Pulling MinIO Docker Image

Before we begin, ensure you have Docker installed on your system. Pull the MinIO Docker image using the following command.

docker pull quay.io/minio/minio

My MinIO Version: RELEASE.2023-07-21T21-12-44Z.

Step 1.2) Spinning up MinIO with Docker

Next, we will run the MiniIO server with Docker using the pulled image.

docker run -dt \
-p 9000:9000 -p 9090:9090 \
-v /local/path/data:/mnt/data \
-v /local/path/config.env:/etc/config.env \
-e "MINIO_CONFIG_ENV_FILE=/etc/config.env" \
--name "minio_local" \
quay.io/minio/minio server --console-address ":9090"

config.env (file)

MINIO_ROOT_USER=myminioadmin

MINIO_ROOT_PASSWORD=minio-secret-key-change-me

MINIO_VOLUMES="/mnt/data"

The MinIO server console (Web interface) can be accessed at http://localhost:9090 and the API will be at localhost:9000.

2. Setup NGINX Proxy server

Step 2.1) Configuring NGINX for console.

Now, let’s proceed with setting up the NGINX web server as reverse proxy for the MiniIO data

docker run -d \
-p 8080:80 \
--name nginx_console \
-v /local/path/nginx_console.conf:/etc/nginx/conf.d/default.conf \
nginx:latest

This will start NGINX proxy server for MinIO Console which can be accessed at http://localhost:8080.

nginx_console.conf (file)

# nginx_console.conf

server {
      listen 80;
      server_name _;

      access_log  /var/log/nginx/minio_console_access.log;  
      error_log   /var/log/nginx/minio_console_error.log;  

      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_set_header X-NginX-Proxy true;

           # This is necessary to pass the correct IP to be hashed
           real_ip_header X-Real-IP;

           proxy_connect_timeout 300;

           # To support websockets in MinIO versions released after January 2023
             proxy_http_version 1.1;
             proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection "upgrade";

           chunked_transfer_encoding off;

           proxy_pass http://localhost:9090; 
}

Step 2.2) Configuring NGINX for API

Similarly, we will configure NGINX to act as a reverse proxy for the MinIO API to ensure secure access to the MinIO server's RESTful API.

docker run -d \
-p 8081:80 \
--name nginx_api \
-v /local/path/nginx_api.conf:/etc/nginx/conf.d/default.conf \
nginx:latest

This will start another NGINX proxy server for MinIO API which can be accessed at http://localhost:8081.

nginx_api.conf (file)

# nginx_api.conf

server {
      listen 80;
      server_name _;

      access_log  /var/log/nginx/minio_api_access.log;  
      error_log   /var/log/nginx/minio_api_error.log;  

      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;
             proxy_set_header Connection "";
             chunked_transfer_encoding off;

           proxy_pass http://localhost:9000;
}

3. Restricting Access to MinIO through NGINX

To prevent unauthorized access to MinIO URL, we will add a following code in NGINX for MinIO API, which will restrict access based on the application’s referer.

# nginx_api.conf

map $http_referer $is_valid_referer {
    default                         0;
    "~^http://localhost:8080/"      1;
    "~^https://my-app-domain.com/"  1;
}

server {

   //  rest of code

   location / {
        if ($is_valid_referer = 0) {
            return 403;
        }

    //  rest of code

   }
}

In the nginx_api.conf file, the 'map' block is defined outside the 'server' block. It maps the 'http_referer' header to the '$is_valid_referer' variable. Here, we add all the valid referer URLs that can access the page.

Next, we use the 'if' condition inside the 'location /' block to check whether the referer is valid or not. If it is not valid, we will return a 403 Forbidden response, effectively denying access to MinIO for unauthorized clients.