Logging POST body (request and response) with ingress-nginx - nginx-location

It seems that out of the box ngx_http_core_module should log request_body if it's part of the log_format.
However, Im not seeing this working for anything that includes auth_request in location block. Im not entirely sure what causes it. It appears to work fine for location blocks that do not include auth_request directive.
Typical the generated location block would look like,
server {
server_name test-api.dummy.co ;
listen 80 ;
listen 443 ssl http2 ;
set $proxy_upstream_name "-";
ssl_certificate_by_lua_block {
certificate.call()
}
proxy_pass_request_body on;
access_log /var/log/nginx/access.log custom_api_log;
location ~* "^/api/v2/management/customer/[a-zA-Z0-9-]+/" {
set $namespace "control";
set $ingress_name "apps-public-gateway-api-ig-auth";
set $service_name "istio-ingressgateway";
set $service_port "80";
set $location_path "/api/v2/management/customer/[a-zA-Z0-9-]+/";
rewrite_by_lua_block {
lua_ingress.rewrite({
force_ssl_redirect = true,
ssl_redirect = true,
force_no_ssl_redirect = false,
use_port_in_redirects = false,
})
balancer.rewrite()
plugins.run()
}
# be careful with `access_by_lua_block` and `satisfy any` directives as satisfy any
# will always succeed when there's `access_by_lua_block` that does not have any lua code doing `ngx.exit(ngx.DECLINED)`
# other authentication method such as basic auth or external auth useless - all requests will be allowed.
#access_by_lua_block {
#}
header_filter_by_lua_block {
lua_ingress.header()
plugins.run()
}
body_filter_by_lua_block {
}
log_by_lua_block {
balancer.log()
monitor.call()
plugins.run()
}
port_in_redirect off;
set $balancer_ewma_score -1;
set $proxy_upstream_name "control-istio-ingressgateway-80";
set $proxy_host $proxy_upstream_name;
set $pass_access_scheme $scheme;
set $pass_server_port $server_port;
set $best_http_host $http_host;
set $pass_port $pass_server_port;
set $proxy_alternative_upstream_name "";
# this location requires authentication
auth_request /_external-auth;
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
auth_request_set $authHeader0 $upstream_http_authorization;
proxy_set_header 'Authorization' $authHeader0;
# Cors Preflight methods needs additional options and different Return Code
if ($request_method = 'OPTIONS') {
more_set_headers 'Access-Control-Allow-Origin: https://test-portal.dummy.co';
more_set_headers 'Access-Control-Allow-Credentials: true';
more_set_headers 'Access-Control-Allow-Methods: GET, PUT, POST, DELETE, PATCH, OPTIONS';
more_set_headers 'Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
more_set_headers 'Access-Control-Max-Age: 3600';
more_set_headers 'Content-Type: text/plain charset=UTF-8';
more_set_headers 'Content-Length: 0';
return 204;
}
more_set_headers 'Access-Control-Allow-Origin: https://test-portal.dummy.co';
more_set_headers 'Access-Control-Allow-Credentials: true';
more_set_headers 'Access-Control-Allow-Methods: GET, PUT, POST, DELETE, PATCH, OPTIONS';
more_set_headers 'Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
client_max_body_size 1m;
proxy_set_header Host $best_http_host;
# Pass the extracted client certificate to the backend
# Allow websocket connections
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header X-Request-ID $req_id;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $full_x_forwarded_proto;
proxy_set_header X-Forwarded-Host $best_http_host;
proxy_set_header X-Forwarded-Port $pass_port;
proxy_set_header X-Scheme $pass_access_scheme;
# Pass the original X-Forwarded-For
proxy_set_header X-Original-Forwarded-For $http_x_forwarded_for;
# mitigate HTTPoxy Vulnerability
# https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/
proxy_set_header Proxy "";
# Custom headers to proxied server
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffering off;
proxy_buffer_size 4k;
proxy_buffers 4 4k;
proxy_max_temp_file_size 1024m;
proxy_request_buffering on;
proxy_http_version 1.1;
proxy_cookie_domain off;
proxy_cookie_path off;
# In case of errors try the next upstream server before returning an error
proxy_next_upstream error timeout;
proxy_next_upstream_timeout 0;
proxy_next_upstream_tries 3;
proxy_pass http://upstream_balancer;
proxy_redirect http://test-api.dummy.co https://test-api.dummy.co;
}
}
Is this achievable via a Lua script perhaps if we cannot achieve this out of the box ?
And how to go about logging the response body without having a custom template for ingress-nginx controller ?
Thanks in advance.

Related

Redirecting url using the ip sent as parameter with the api call on nginx. /status/ip -> http://ip:4444/status

I'm trying to get a response back from an api call to the machine.
ex: http://ip:4444/status
My internal api looks something like this : /status/ip.
I want my api calls from /status/ip to pick up the ip as a parameter and use it as a url like http://ip:4444/status and get a response from there.
Currently I have a failed implementation of nginx server that looks like this:
location /status/ {
if ($args ~* "/status/param1=val1") {
rewrite ^ http://$arg_param1:4444/status redirect;
}
proxy_pass http://ggr;
add_header Access-Control-Allow-Origin '*' always;
add_header Access-Control-Allow-Headers 'X-Requested-With,Content-Type' always;
add_header Cross-Origin-Resource-Policy 'cross-origin' always;
proxy_http_version 1.1;
proxy_set_header Connection "";
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_connect_timeout 10;
proxy_send_timeout 300;
proxy_read_timeout 300;
proxy_buffers 32 64m;
proxy_buffer_size 64k;
proxy_next_upstream error timeout http_502 http_503 http_504;
client_max_body_size 64m;
client_body_buffer_size 64m;
}
I'm expecting <localhost:3000/status/ip> to redirect me to <ip:4444/status>.

Nginx how to set proxy pass using map based on the host and location

Currently I setting proxy pass dynamicaly using map directive. Now I would like for a specifc location /dcs/ I would like to set a different proxy pass rather than one mentioned through map.conf and proxy_pass.map. Is there a way to say use different map for a specific location /dcs/.
Should just use If stantement like if host is dev-api.mysite.com then set proxypass to http://abcdef.com. Is this the only option.
MySite.conf
server {
listen 80;
server_name ~^(api|dev-api|staging-api)\.mysite\.com$;
location / {
proxy_set_header Cookie "";
add_header Strict-Transport-Security "max-age=0; includeSubDomains" always;
proxy_hide_header 'Access-Control-Allow-Origin';
add_header 'Access-Control-Allow-Origin' '*' always;
include /etc/nginx/conf.d/headers.inc;
}
location /dcs/ {
set $proxy_host $host;
set $proxy_pass Set-different-host based of dev,staging,prod;
proxy_set_header Cookie "";
add_header Strict-Transport-Security "max-age=0; includeSubDomains" always;
proxy_hide_header 'Access-Control-Allow-Origin';
add_header 'Access-Control-Allow-Origin' '*' always;
include /etc/nginx/conf.d/headers.inc;
}
}
maps.conf
map $host $proxy_pass {
hostnames;
include /etc/nginx/conf.d/proxy_pass.map;
}
proxy_pass.map
# mysite.com
api.mysite.com myst.plat:8080;
staging-api.mysite.com staging.myst.plat:8081;
dev-api.mysite.com dev.plat:8080;
headers.inc
proxy_pass $upstream_proto://$proxy_pass;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header X-Forwarded-Proto $real_scheme;
proxy_set_header X-Forwarded-Scheme $real_scheme;
proxy_set_header Host $proxy_host;
proxy_ssl_session_reuse on;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-GeoIP-Country-Code $geoip_country_code;
proxy_set_header X-Site-Id $site_id;
add_header X-Cache-Time $date_gmt;
add_header X-Cache-Date $upstream_http_date;
add_header X-Proxy-Cache $upstream_cache_status;
# add_header X-Site-Id $site_id;
You can use several chained map blocks:
map $host $proxy_by_host {
hostnames;
include /etc/nginx/conf.d/proxy_pass.map;
}
map $uri $proxy_pass {
~^/dcs/ abcdef.com;
default $proxy_by_host;
}
Update
Looks like I slightly misunderstood your question, to use two different map blocks depending on request URI, use something like
map $host $proxy_by_host {
hostnames;
include /etc/nginx/conf.d/proxy_pass.map;
}
map $host $dcs_proxy_by_host {
hostnames;
include /etc/nginx/conf.d/dcs_proxy_pass.map;
}
map $uri $proxy_pass {
~^/dcs/ $dcs_proxy_by_host;
default $proxy_by_host;
}
This way if all the other parameters for your locations are equal, you can use only one location / { ... } for all requests.

nginx reverse proxy disable cache

i use nginx as a reverse proxy to connect a api. The problem is when i send a query after add or remove something. Nginx send me the old json value. I tried to disabled cache but it's not working.
my nginx config:
location / {
sendfile off;
add_header Last-Modified $date_gmt;
add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
if_modified_since off;
expires off;
etag off;
proxy_no_cache 1;
proxy_cache_bypass 1;
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header HTTPS $https;
}
i tried query without nginx and all work well in console
thank you!
According to the documentation proxy_cache you have to replace
proxy_no_cache 1;
proxy_cache_bypass 1;
proxy_no_cache and proxy_cache_bypass defines conditions under which the response will not be saved to a cache.
Then to disable the cache, you can replace these two condition with
proxy_cache off;
Here a full exemple that you can use to configure a proxy for a stateless api server
location /myapi {
# Proxy
proxy_set_header X-Localhost true;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://localhost:8080/myapi;
proxy_redirect off;
proxy_buffers 32 16k;
proxy_busy_buffers_size 64k;
proxy_cache off;
# Headers for client browser NOCACHE + CORS origin filter
add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
expires off;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept' always;
allow all;
}

Nginx not honoring proxy_cache_valid

I have a nginx cache configure as follows:
location / {
rewrite ^/(.*)$ /$1 break;
proxy_pass http://news;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_set_header X-Protocol http;
proxy_read_timeout 480;
proxy_connect_timeout 480;
set $cache_key "$uri";
proxy_cache my-cache;
proxy_ignore_headers X-Accel-Expires Expires Cache-Control;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 30s;
proxy_cache_methods GET;
add_header X-Cache-Status $upstream_cache_status;
}
When I check the x-cache-status header of the response the second time its value is HIT. The problem is that the after about 20 seconds the response is giving MISS. The http response code is 200. Any ideas?
Be aware of the inactive=time setting in the nginx.conf for proxy_cache_path. Proxy_cache_invalid does not override this value.

Limit HTTP verbs without redundant config

I've got an Elasticsearch cluster plus Logstash and Kibana, and I only want to expose a read-only window into the indexes, with the exception of the index kibana-int so that dashboards can be saved.
I've found a suitable ES proxy config, and I've modified it to use limit_except to disallow write/modify to other indexes, but much of the config is needlessly duplicated. Is there a cleaner way to define this?
upstream elasticsearch {
server es-01.iad.company.com:9200;
server es-02.iad.company.com:9200;
}
server {
listen 9200;
server_name elasticsearch.proxy;
client_max_body_size 50m;
location / {
limit_except GET POST HEAD OPTIONS {
deny all;
}
proxy_pass http://elasticsearch;
proxy_redirect off;
proxy_set_header Connection "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_pass_header Access-Control-Allow-Origin;
proxy_pass_header Access-Control-Allow-Methods;
proxy_hide_header Access-Control-Allow-Headers;
add_header Access-Control-Allow-Headers 'X-Requested-With, Content-Type';
add_header Access-Control-Allow-Credentials true;
}
location /kibana-int/ {
proxy_pass http://elasticsearch;
proxy_redirect off;
proxy_set_header Connection "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_pass_header Access-Control-Allow-Origin;
proxy_pass_header Access-Control-Allow-Methods;
proxy_hide_header Access-Control-Allow-Headers;
add_header Access-Control-Allow-Headers 'X-Requested-With, Content-Type';
add_header Access-Control-Allow-Credentials true;
}
}
There are several ways:
Solution 1
You could put repeating config into file and include it.
Your config:
upstream elasticsearch {
server es-01.iad.company.com:9200;
server es-02.iad.company.com:9200;
}
server {
listen 9200;
server_name elasticsearch.proxy;
client_max_body_size 50m;
location / {
limit_except GET POST HEAD OPTIONS {
deny all;
}
include proxy.inc;
}
location /kibana-int/ {
include proxy.inc;
}
}
proxy.inc:
proxy_pass http://elasticsearch;
proxy_redirect off;
proxy_set_header Connection "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_hide_header Access-Control-Allow-Headers;
add_header Access-Control-Allow-Headers 'X-Requested-With, Content-Type';
add_header Access-Control-Allow-Credentials true;
Solution 2
Other way is use nginx's directive inheritance.
upstream elasticsearch {
server es-01.iad.company.com:9200;
server es-02.iad.company.com:9200;
}
server {
listen 9200;
server_name elasticsearch.proxy;
client_max_body_size 50m;
proxy_redirect off;
proxy_set_header Connection "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_hide_header Access-Control-Allow-Headers;
add_header Access-Control-Allow-Headers 'X-Requested-With, Content-Type';
add_header Access-Control-Allow-Credentials true;
location / {
limit_except GET POST HEAD OPTIONS {
deny all;
}
proxy_pass http://elasticsearch;
}
location /kibana-int/ {
proxy_pass http://elasticsearch;
}
}
BTW, your proxy_pass_header directives are needless. Nginx proxies almost all headers by default.

Resources