X-Forwarded-For in IIS and ELK
Bringing a custom request header into IIS logs and the ELK stack
Now that I got my public site logs in to my ELK stack I wanted to get the remote IP tied to my IIS log. I use NGINX to act as a proxy so all of the remote ip addresses were my NGINX server and not the client. I was already passing the remote IP from NGINX back to IIS in an x-forwarded-for
header and now I needed to get that into ELK.
So, I fiddled with it and found that IIS in Server 2016 supports adding in arbitrary fields into the log files. It supports a few different data sources, namely, Request Header
. It also supports Server Variable
and Response Header
.
I want to put the value of the X-Forwarded-For
header onto the IIS log entry, so I added that custom field to the logging fields in IIS. I used X-Forwarded-For
for the Field Name
and Source
and left the Source Type
as Request Header
. The Field Name
box is a drop down, but you can free form type text into it. The drop down is a list of well-known headers.
To add the header to the logs,
- Open IIS Manager
- Expand your server
- Expand Sites
- Click your web site
- Double click
Logging
- Click
Select Fields
- Click
Add Field...
at the bottom of the popup - Put
X-Forwarded-For
into theField Name
andSource
text boxes. - Leave
Source Type
asRequest Header
- Click
OK
out of the Add Custom Field box - Click
OK
out of the W3C Logging Fields box - Click
Apply
on the right.
Now that we have the new field in the logs we will need to modify the grok pattern that ElasticSearch uses to parse the logs. To see how to do that, please go through my previous post: File Beat and IIS with multiple sites
For this exercise, since I am setting up all of my IIS sites the same way on all of my servers, I will also remove all of the extra grok patterns for performance. My server is relatively low power and I need to squeeze every extra cycle I can out of the poor CPU's.
The new and only grok pattern that I am using is:
%{TIMESTAMP_ISO8601:iis.access.time} %{NOTSPACE:iis.access.site_name} %{NOTSPACE:iis.access.server_name} %{IPORHOST:iis.access.server_ip} %{WORD:iis.access.method} %{URIPATH:iis.access.url} %{NOTSPACE:iis.access.query_string} %{NUMBER:iis.access.port} %{NOTSPACE:iis.access.user_name} %{IPORHOST:iis.access.proxy_ip} HTTP/%{NUMBER:iis.access.http_version} %{NOTSPACE:iis.access.agent} %{NOTSPACE:iis.access.referrer} %{NOTSPACE:iis.access.hostname} %{NUMBER:iis.access.response_code} %{NUMBER:iis.access.sub_status} %{NUMBER:iis.access.win32_status} %{NUMBER:iis.access.body_sent.bytes} %{NUMBER:iis.access.body_received.bytes} %{NUMBER:iis.access.request_time_ms} %{IPORHOST:iis.access.remote_ip}
If you compare it to the one I created in the previous post, I added a new field, iis.access.proxy_ip
and moved the iis.access.remote_ip
to the end of the list. This effectively created a new field where the remote_ip
was, calling it the proxy_ip
, since it contains the proxy address it made sense. At work, with multiple proxies I could definitely see this being a benefit since we have multiple load balancers in multiple data-centers. Since I kept the same name for the iis.access.remote_ip
attribute all of the dashboards continued to work the same way (except they actually worked now). To get rid of the exclamation point on the new field in the Kibana dashboard I did need to go to the management of the Kibana index and refresh the field list.