javaamazon-web-servicesspring-bootnginxmultipartform-data

AWS Spring boot Nginx - File upload 1KB works - 250KB fails with 403 Forbidden


I have a spring boot app running on AWS that uploads a file to S3 which works fine for 1KB txt file. It fails for a 250KB txt with 403 Forbidden with out any logs on the nginx access/error logs, request doesn't reach Tomcat as well. I even removed saving to disk/S3, as below. It immediatly fails with 403 Forbidden.

Controller

    @GetMapping("/inputuploads") 
    public String listInputUploads(Model model) {
        logger.info("Renedering upload page");
        return "inputuploads";
    }
    
    @PostMapping("/inputuploads")
    public String saveFile(Model model, @RequestParam("file") MultipartFile file, RedirectAttributes ra) throws IOException {       
        logger.info("Saving file:"+file.getOriginalFilename());
        ra.addFlashAttribute("message","Uploaded file: "+ file.getOriginalFilename());      
        return "redirect:/inputuploads";
    }

template

            <div th:if="${message}">
                    <div th:text="${message}">                    
                    </div>                              
            </div>
                       
            <div >
                <form method="post" th:action="@{/inputuploads}" enctype="multipart/form-data" >
                                    
                    <div>                        
                        <input type="file" name="file" accept=".xlsm" >                                          
                    </div>
                    
                    <div>
                        <button type="submit" >Upload</button>
                    </div>
                    
                </form>                
            </div>
        

application.yml

spring:
  servlet:
    multipart:
      max-file-size: 100MB
      max-request-size: 100MB
      
server:
  port: 5000
  tomcat:
    max-swallow-size: -1 

.platform/nginx/nginx.conf

user                    nginx;
error_log               /var/log/nginx/error.log warn;
pid                     /var/run/nginx.pid;
worker_processes        auto;
worker_rlimit_nofile    32633;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    include       conf.d/*.conf;

    map $http_upgrade $connection_upgrade {
        default     "upgrade";
    }

    server {
        listen 80;
        return 301 https://$host$request_uri;
    }

    server {
        listen 443 ssl default_server;

        ssl_certificate certificates/localhost.crt;
        ssl_certificate_key certificates/localhost.key;
        

        access_log    /var/log/nginx/access.log main;
        
        client_header_timeout 1d;
        client_body_timeout   1d;
        client_max_body_size 100M;
        keepalive_timeout     1d;
        proxy_connect_timeout 1d;
        proxy_read_timeout 1d;
        proxy_send_timeout 1d;
        gzip                  off;
        gzip_comp_level       4;
        gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;

        include conf.d/elasticbeanstalk/*.conf;
    }
}

.platform/nginx/conf.d/client_max_body_size.conf

client_max_body_size 100M;

.platform/nginx/conf.d/elasticbeanstalk/00_application.conf

location / {
    proxy_pass http://127.0.0.1:5000;

    proxy_http_version  1.1;

    proxy_set_header    Connection          $connection_upgrade;
    proxy_set_header    Upgrade             $http_upgrade;
    proxy_set_header    Host                $host;
    proxy_set_header    X-Real-IP           $remote_addr;
    proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
}

.ebextensions/00-set-timezone.config

commands:
  set_time_zone:
    command: ln -f -s /usr/share/zoneinfo/Asia/Kolkata /etc/localtime

Solution

  • Since the HTTP requests don't get nginx (expect for the small ones) and the error is 403 Forbidden, it seems that AWS WAF (which can be attached to the frontend you are using, i.e. AWS ALB) is blocking the requests. By default, AWS WAF blocks requests if the HTTP body size is greater than 8 KB. You can change this behavior by configuring Oversize body handling.

    So in this case, you can add a rule to the WAF ACL to specifically exclude the URI used for uploads. From Guidelines for managing oversize components in your web ACL:

    If you need to allow some requests with oversize component contents, if possible, add rules to explicitly allow only those requests. Prioritize those rules so that they run before any other rules in the web ACL that inspect the same component types. With this approach, you won't be able to use AWS WAF to inspect the entire contents of the oversize components that you allow to pass to your protected resource.