AWS ElasticBeanstalk Nginx rate limiting not working for Nodejs application - node.js

This is the file rate-limit.nginx.config which I placed in the .ebextensions folder of my Node app.
files:
"/etc/nginx/conf.d/00_proxy.conf":
mode: "000755"
owner: root
group: root
content: |
upstream nodejs_new {
server 127.0.0.1:8081;
keepalive 256;
}
limit_req_zone $binary_remote_addr zone=loginlimit:10m rate=5r/s;
server {
listen 8080;
if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})T(\d{2})") {
set $year $1;
set $month $2;
set $day $3;
set $hour $4;
}
access_log /var/log/nginx/healthd/application.log.$year-$month-$day-$hour healthd;
access_log /var/log/nginx/access.log main;
location / {
proxy_pass http://nodejs_new;
proxy_set_header Connection "";
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
gzip on;
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;
location /.well-known/acme-challenge {
alias /var/app/current/public/letsencrypt;
}
location /auth/generateotp/ {
limit_req zone=loginlimit;
}
location /auth/verifyotp/ {
limit_req zone=loginlimit;
}
}
container_commands:
01_reload_nginx:
command: "sudo service nginx reload"
02_remove_config:
command: "rm -f /tmp/deployment/config/#etc#nginx#conf.d#00_elastic_beanstalk_proxy.conf /etc/nginx/conf.d/00_elastic_beanstalk_proxy.conf"
Once I deployed this, I have ssh'd into the instance, and made sure that the /etc/nginx/conf.d/00_proxy.conf file exists.
I am testing the /auth/generateotp with ab tool, and the API accepts any number of concurrent requests, whereas its supposed to accept only 5 requests in one second.
What could be the issue here?

Related

Unable to access Elastic Beanstalk (single instance) from custom domain HTTPS

Greetings SO community,
I am attempting to configure my single-instance Elastic Beanstalk application to use a custom domain and HTTPS. Both the custom domain and SSL certificate were obtained from a third-party and uses their DNS servers (rather than Route 53).
I have added the .ebextensions/https-instance-securitygroup.config per AWS documentation (https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/https-singleinstance.html) as well as the files for Node application (https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/https-singleinstance-nodejs.html). The only difference in the last step is that I did not create a .ebextensions/https-instance.config file as I am pushing my code to GitHub and using CodePipeline to build my code. So, the https.conf and certificates were manually created and uploaded to the EC2 instance.
Also, I have checked my instance's inbound rules to ensure that 80 & 443 are open on the EB instance and for the associated security group.
proxy.conf
upstream nodejs {
server 127.0.0.1:5000;
keepalive 256;
}
server {
listen 8080;
if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})T(\d{2})") {
set $year $1;
set $month $2;
set $day $3;
set $hour $4;
}
access_log /var/log/nginx/healthd/application.log.$year-$month-$day-$hour healthd;
access_log /var/log/nginx/access.log main;
location / {
proxy_pass http://nodejs;
proxy_set_header Connection "";
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
gzip on;
gzip_comp_level 4;
gzip_types text/html text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
location /static {
alias /var/app/current/client/build/static;
}
}
https.conf
# HTTPS server
server {
listen 443 ssl;
server_name localhost;
ssl_certificate /etc/pki/tls/certs/server.crt;
ssl_certificate_key /etc/pki/tls/certs/server.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
# For enhanced health reporting support, uncomment this block:
#if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})T(\d{2})") {
# set $year $1;
# set $month $2;
# set $day $3;
# set $hour $4;
#}
#access_log /var/log/nginx/healthd/application.log.$year-$month-$day-$hour healthd;
#access_log /var/log/nginx/access.log main;
location / {
proxy_pass http://nodejs;
proxy_set_header Connection "";
proxy_http_version 1.1;
proxy_set_header Host $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 https;
}
}
So after rereading the AWS documentation for what felt like the hundredth time, I was finally able to resolve my issues. And because I wasn't strictly using the preferred method of using an .ebextensions folder, I had to fiddle with the Nginx proxy on the Elastic Beanstalk-created EC2 instance directly.
In short, I was missing the following section from my /etc/nginx/conf.d/proxy.conffile:
location / {
### START MISSING ###
set $redirect 0;
if ($http_x_forwarded_proto != "https") {
set $redirect 1;
}
if ($http_user_agent ~* "ELB-HealthChecker") {
set $redirect 0;
}
if ($redirect = 1) {
return 301 https://$host$request_uri;
}
### END OF MISSING ###
proxy_pass http://nodejs;
proxy_set_header Connection "";
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
This documented here: https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/configuring-https-httpredirect.html
Specifically, This is a snippet from the NodeJS, default Nginx proxy config file: https://github.com/awsdocs/elastic-beanstalk-samples/blob/master/configuration-files/aws-provided/security-configuration/https-redirect/nodejs/https-redirect-nodejs.config

Nginx caching with Azure Storage Blob

Local caching works fine, however trying to cache files server by the Azure Blob Storage is not working.
Each request the Signature changes and it returns no caching headers.
I think its because of this signature at the end? Anyone got an idea how to surpass that?
https://accountname.blob.core.windows.net/static/assets/js/jquery331.min.js?se=2020-02-10T15%3A06%3A35Z&sp=r&sv=2019-09-28&sr=b&sig=<Signature>
Below is my nginx config. It mostly based on this one = https://serversforhackers.com/c/nginx-caching
upstream app {
server web:8000;
}
proxy_cache_path ./cache keys_zone=one:10m loader_threshold=300 loader_files=200;
proxy_cache_key "$scheme$request_method$host$request_uri";
# Expires map
map $sent_http_content_type $expires {
default off;
text/css max;
application/javascript max;
~image/ max;
}
server {
include mime.types;
listen 80;
listen [::]:80;
root /usr/src/application/;
index /usr/src/application/main/;
gzip on;
gzip_comp_level 2;
gzip_http_version 1.0;
gzip_proxied any;
gzip_min_length 1100;
gzip_buffers 16 8k;
gzip_types text/plain text/html text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript script text/script;
gzip_disable "MSIE [1-6].(?!.*SV1)";
gzip_vary on;
location / {
proxy_pass http://app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
location /staticfiles/ {
proxy_cache one;
proxy_buffering on;
add_header X-Proxy-Cache $upstream_cache_status;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Authorization '';
proxy_set_header Host https://accountname.blob.core.windows.net/static;
proxy_pass https://accountname.blob.core.windows.net;
}
location /media/ {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Authorization '';
proxy_set_header Host https://accountname.blob.core.windows.net/media;
proxy_pass https://accountname.blob.core.windows.net;
}
# cache.appcache, your document html and data
location ~* \.(?:manifest|appcache|html?|xml|json)$ {
expires -1;
}
# Feed
location ~* \.(?:rss|atom)$ {
expires 1h;
add_header Cache-Control "public";
}
# Media: images, icons, video, audio, HTC
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
expires 1M;
access_log off;
add_header Cache-Control "public";
}
# CSS and Javascript
location ~* \.(?:css|js)$ {
expires 1y;
access_log off;
add_header Cache-Control "public";
}
location /media/ {
alias /usr/src/application/main/media;
}
proxy_connect_timeout 300s;
proxy_read_timeout 300s;
proxy_next_upstream error timeout http_502;
location /index/ {
alias /usr/src/application/main/;
}
}
So I've found what caused the query strings. We are using django storages, in that the option for a variable AZURE_URL_EXPIRATION_SECS is possible which appends a query string.
As of now caching works without CDN. However, I am surprised that with the CDN active and rules set to ignore query strings no caching was happening.

NGINX Browser Caching Not Working - Node JS EC2

Went through several articles but cannot figure out why the browser caching isnt working. I am using prerender.io as well as SSL:
gzip on;
gzip_min_length 500;
gzip_proxied any;
gzip_comp_level 4;
gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/json application/xml application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
gzip_vary on;
gzip_disable "msie6";
server {
listen 8080 default_server;
listen [::]:8080 default_server;
server_name <servername> www.<servername>.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2 default_server;
server_name <servername> www.<servername>.com;
ssl_certificate /etc/pki/tls/private/<servername>.com.chained.crt;
ssl_certificate_key /etc/pki/tls/private/private.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers <ssl_ciphers_code>
ssl_session_cache shared:SSL:5m;
ssl_session_timeout 1h;
add_header Strict-Transport-Security "max-age=15768000" always;
root /var/app/current;
location / {
proxy_set_header X-Prerender-Token iKJwgCElYIfxtt9u99Zg;
set $prerender 0;
if ($http_user_agent ~* "baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator") {
set $prerender 1;
}
if ($args ~ "_escaped_fragment_") {
set $prerender 1;
}
if ($http_user_agent ~ "Prerender") {
set $prerender 0;
}
if ($uri ~* "\.(js|css|xml|less|png|jpg|jpeg|gif|pdf|doc|txt|ico|rss|zip|mp3|rar|exe|wmv|doc|avi|ppt|mpg|mpeg|tif|wav|mov|psd|ai|xls|mp4|m4a|swf|dat|dmg|iso|flv|m4v|torrent|ttf|woff|svg|eot)") {
set $prerender 0;
}
#resolve using Google's DNS server to force DNS resolution and prevent caching of IPs
resolver 8.8.8.8;
if ($prerender = 1) {
#setting prerender as a variable forces DNS resolution since nginx caches IPs and doesnt play well with load balancing
set $prerender "service.prerender.io";
rewrite .* /$scheme://$host$request_uri? break;
proxy_pass http://$prerender;
}
# Proxy_pass configuration
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_max_temp_file_size 0;
proxy_pass http://0.0.0.0:3000;
proxy_redirect off;
proxy_read_timeout 240s;
}
location ~* \.(ico|css|js|gif|jpeg|jpg|png|woff|ttf|otf|svg|woff2|eot)$ {
root /var/app/current/app/dist/client/; #if i comment this out it, my css and js files are not found...
expires 30d;
access_log off;
log_not_found off;
add_header Pragma "public";
add_header Cache-Control "public";
}
# Increase http2 max sizes
http2_max_field_size 64k;
http2_max_header_size 64k;
client_max_body_size 4G;
keepalive_timeout 10;
}
My assets dir is as follows:
JS: /var/app/current/app/dist/client/js
CSS: /var/app/current/app/dist/client/assets/css
Images: /var/app/current/app/dist/client/assets/graphics
Fonts: /var/app/current/app/dist/client/assets/fonts
Videos: /var/app/current/app/dist/client/assets/videos
UPDATED CONFIG:
gzip on;
gzip_min_length 500;
gzip_proxied any;
gzip_comp_level 4;
gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/json application/xml application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
gzip_vary on;
gzip_disable "msie6";
server {
listen 8080 default_server;
listen [::]:8080 default_server;
server_name <servername.com> <www.servername.com>;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2 default_server;
server_name <servername.com> <www.servername.com>;
ssl_certificate /etc/pki/tls/private/<servername>.com.chained.crt;
ssl_certificate_key /etc/pki/tls/private/private.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers <ciphers>;
ssl_session_cache shared:SSL:5m;
ssl_session_timeout 1h;
add_header Strict-Transport-Security "max-age=15768000" always;
root /var/app/current;
location / {
proxy_set_header X-Prerender-Token <token> ;
set $prerender 0;
if ($http_user_agent ~* "developers\.google\.com|googlebot|gigabot|yeti|yandex|ia_archiver|baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator") {
set $prerender 1;
}
if ($args ~ "_escaped_fragment_") {
set $prerender 1;
}
if ($http_user_agent ~ "Prerender") {
set $prerender 0;
}
if ($uri ~* "\.(js|css|xml|less|png|jpg|jpeg|gif|pdf|doc|txt|ico|rss|zip|mp3|rar|exe|wmv|doc|avi|ppt|mpg|mpeg|tif|wav|mov|psd|ai|xls|mp4|m4a|swf|dat|dmg|iso|flv|m4v|torrent|ttf|woff|svg|eot)") {
set $prerender 0;
}
#resolve using Google's DNS server to force DNS resolution and prevent caching of IPs
resolver 8.8.8.8;
if ($prerender = 1) {
#setting prerender as a variable forces DNS resolution since nginx caches IPs and doesnt play well with load balancing
set $prerender "service.prerender.io";
rewrite .* /$scheme://$host$request_uri? break;
proxy_pass http://$prerender;
}
# Proxy_pass configuration
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_max_temp_file_size 0;
proxy_pass http://0.0.0.0:3000;
proxy_redirect off;
proxy_read_timeout 240s;
}
location ~* \.(ico|css|js|gif|jpeg|jpg|png|woff|ttf|otf|svg|woff2|eot)$ {
root /var/app/current/app/dist/client/; #if i comment this out it, my css and js files are not found...
expires 30d;
access_log off;
log_not_found off;
add_header Pragma "public";
add_header Cache-Control "public";
}
location /assets/graphics/ {
proxy_ignore_headers Cache-Control;
proxy_cache_valid any 30d;
}
# Increase http2 max sizes
proxy_buffers 8 16k;
proxy_buffer_size 32k;
http2_max_field_size 64k;
http2_max_header_size 64k;
client_max_body_size 4G;
keepalive_timeout 10;
}
In your NGINX configuration, you set the cache expiration to 30 days for your images with the line:
expires 30d;
However looking at your images coming from your server, the max-age of the images is set to 0 which is causing your browser to re-pull the images on a refresh (below image was after a refresh):
I suspect that NGINX is acting as a proxy to an origin server that is part of your solution. This origin server is setting the max-age to 0 in the cache-control header and NGINX is respecting that setting.
Per the NGINX caching guide:
By default, NGINX respects the Cache-Control headers from origin servers. It does not cache responses with Cache-Control set to Private, No-Cache, or No-Store or with Set-Cookie in the response header. NGINX only caches GET and HEAD client requests.
To override the cache-control set at the origin server and set the max-age to 30d, use the NGINX proxy_ignore_headers and proxy_cache_valid directive like so:
...
location /assets/graphics/ {
proxy_ignore_headers Cache-Control;
proxy_cache_valid any 30d;
...
}
...
The code in my solution is taken directly from the NGINX caching guide and modified to your configuration.
Or determine how to change the cache control headers at the origin server.
UPDATE
After you updated your NGINX config, your images in the /assets/graphics/ directory are pulled from local browser memory and have expiration of 30 days (2595200) as seen below. Yesterday, they were all being pulled from your server and not being cached. This solution solves your problem. For the rest of the assets that you want cached, you need to further change your config to also cache these according to your requirements.

Extend nginx on elastic beanstalk with websockets

I want to set up an elastic beanstalk app to work with HTTP and Websockets. I can get the HTTP working on port 8081, but I can't access my websocket server because I get a 301 redirect error with nginx. I can tell my websocket server is running, does anyone know why I can't access it? Here is my proxy.config file:
upstream nodejs {
server 127.0.0.1:8081;
keepalive 256;
}
upstream wsserver {
server 127.0.0.1:3000;
keepalive 256;
}
server {
listen 8080;
if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})T(\d{2})") {
set $year $1;
set $month $2;
set $day $3;
set $hour $4;
}
access_log /var/log/nginx/healthd/application.log.$year-$month-$day-$hour healthd;
access_log /var/log/nginx/access.log main;
# prevents 502 bad gateway error
large_client_header_buffers 8 32k;
location /ws/ {
# prevents 502 bad gateway error
proxy_buffers 8 32k;
proxy_buffer_size 64k;
proxy_pass http://wsserver;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}
location / {
proxy_pass http://nodejs;
proxy_set_header Connection "";
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
gzip on;
gzip_comp_level 4;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
}

Turn off Nginx Gzip for a specific query string (used with nodejs)

I'm using nginx to gzip static files & json responses from a nodejs server.
For one specific request (with a query string like "?fn=foo"), I need to send a non-gzip json response.
I've tried to achieve this with nginx location module, based on a regex on the query string, but the query string is not in the URI used to match location by nginx
I've tried to put a if ($arg_fn = "foo") {gzip off;} in my main location route, but it fails with a 404 instead.
Any idea?
Is it possible to achieve this with nginx? or is there a way to tell from nodejs to nginx not to gzip this response?
My nginx conf file:
worker_processes 1;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=one:8m max_size=3000m inactive=600m;
proxy_temp_path /var/cache/nginx/proxy_temp;
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"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
gzip on;
gzip_comp_level 6;
gzip_vary on;
gzip_min_length 1000;
gzip_proxied any;
gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript application/json;
gzip_buffers 16 8k;
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream upstream {
server 127.0.0.1:8887;
server 127.0.0.1:8888;
keepalive 64;
}
server {
listen 80;
listen 443 ssl;
server_name _;
ssl_certificate /etc/nginx/ssl/zellno-ssl-bundle.crt;
ssl_certificate_key /etc/nginx/ssl/zellno-key.pem;
location / {
if ($arg_fn = "comp") {
gzip off;
}
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://upstream/;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
}
error_page 404 /404.html;
location = /40x.html {
root /usr/share/nginx/html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}
I think your issue is that you said $args_fn with an "s", while it should be $arg_fn in singular form, try that and tell me how it goes.
EDIT:
Try this to make sure that you enter the if block
location / {
if ($arg_fn = "comp") {
return 444;
#gzip off;
}
}
If it returns the error code then the if is working but not the gzip, if it doesn't work then we need to fix the if first

Resources