Microsoft NPS Server creates logs via EventLog and logfiles.
Capturing the Event Logs is pretty straight forward with a tool like NXLog, but parsing the Logfile is more complicated, so I want to share how I did it. I’m not using extractors because we use Graylog Forwarders in our environment and you can’t use them together. If you’re using NXLogEE you can use the nps extension and skip this guide, otherwise keep reading.
first step is to setup the NPS logfile format.
There you have to click on Logfile Properties and change the Format to ODBC(Legacy). This way the logfiles will be created in csv format and be compliant to the documentation here: Interpret NPS Database Format Log Files | Microsoft Learn
Next step is to send the Logfile to your Graylog environment. Create an index, input and stream for your NPS logs the way you do it normally in your environment. I’m using a seperate input, stream and index for the NPS logs but thats not mandatory.
To send the logfiles to graylog I’m using NXLog.
Here is the nxlog.conf
Panic Soft
#####################################################################
##Default Definitions################################################
#####################################################################
define ROOT C:\Program Files\nxlog
define CERTDIR %ROOT%\cert
define CONFDIR %ROOT%\conf
define LOGDIR %ROOT%\data
define LOGFILE %LOGDIR%\nxlog.log
LogFile %LOGFILE%
Moduledir %ROOT%\modules
CacheDir %ROOT%\data
Pidfile %ROOT%\data\nxlog.pid
SpoolDir %ROOT%\data
#####################################################################
##Custom Definitions#################################################
#####################################################################
##Destination Server:
define DESTSRV 10.100.248.171
##Destination Port:
define DESTPORT 3510
define DESTPORTNAP 3515
#####################################################################
##Extensions#########################################################
#####################################################################
##Logrotation
<Extension _fileop>
Module xm_fileop
# Check the size of our log file hourly, rotate if larger than 5MB
<Schedule>
Every 1 hour
Exec if (file_exists('%LOGFILE%') and \
(file_size('%LOGFILE%') >= 5M)) \
file_cycle('%LOGFILE%', 8);
</Schedule>
# Rotate our log file every week on Sunday at midnight
<Schedule>
When @weekly
Exec if file_exists('%LOGFILE%') file_cycle('%LOGFILE%', 8);
</Schedule>
</Extension>
##GELF Extension Module
<Extension _gelf>
Module xm_gelf
</Extension>
#####################################################################
##Inputs#############################################################
#####################################################################
<Input EventLogs>
Module im_msvistalog
<QueryXML>
<QueryList>
<Query Id='0'>
<Select Path='Application'>*</Select>
<Select Path='Security'>*</Select>
<Select Path='System'>*</Select>
<Select Path='Setup'>*</Select>
</Query>
</QueryList>
</QueryXML>
</Input>
<Input z000nap01_logfiles>
Module im_file
#File 'C:\\Program Files\\Microsoft Configuration Manager\\Logs\\*.log'
File 'C:\\Windows\\System32\\LogFiles\\IN*.log'
SavePos TRUE
ReadFromLast TRUE
PollInterval 1
Exec $Message = $raw_event; $SyslogFacilityValue = 22;
Exec $logname = file_basename(file_name());
</Input>
#####################################################################
##Outputs############################################################
#####################################################################
<Output graylog>
Module om_tcp
Host %DESTSRV%
Port %DESTPORT%
OutputType GELF_TCP
</Output>
<Output graylogNAP>
Module om_tcp
Host %DESTSRV%
Port %DESTPORTNAP%
OutputType GELF_TCP
</Output>
#####################################################################
##Routes#############################################################
#####################################################################
<Route graylog_route>
Path EventLogs => graylog
</Route>
<Route graylog_route>
Path z000nap01_logfiles => graylogNAP
</Route>
DESTPORT variable is the port for the windows event logs. Use the port you’re using for windows events input in your environment.
DESTPORTNAP variable is the port for the nps logfile. Use the port for your newly created input.
Now your NPS server should send the logfiles to graylog, but they are not split into fields. to change that we need a pipeline rule and a pipeline.
Create a new rule under system/pipeline/manage rules/create rule.
Here is the parser:
rule "MS NPS Logfile Parser"
// Parses Logfiles created via Microsoft NPS Server.
// Logfile format has to be ODBC(Legacy)
when
has_field("message")
then
let m = to_string($message.full_message);
//add some empty csv entries for shorter logs that are not compliant to the
//MS documentation and a character that does not get parsed to prevent out
//of bound errors
let m = regex_replace(",$", to_string(m), ",,,x");
let m = split(",", to_string(m));
set_field("nps_computername", m[0]);
set_field("nps_servicename", m[1]);
set_field("nps_date", m[2]);
set_field("nps_time", m[3]);
set_field("nps_packettype", m[4]);
set_field("nps_username", m[5]);
set_field("nps_fqdn", m[6]);
set_field("nps_calledstationID", m[7]);
set_field("nps_callingstationID", m[8]);
set_field("nps_callbacknumber", m[9]);
set_field("nps_framedIP", m[10]);
set_field("nps_NASidentifier", m[11]);
set_field("nps_NASIP", m[12]);
set_field("nps_NASport", m[13]);
set_field("nps_clientvendor", m[14]);
set_field("nps_clientIP", m[15]);
set_field("nps_clientfriendlyname", m[16]);
set_field("nps_eventtimestamp", m[17]);
set_field("nps_portlimit", m[18]);
set_field("nps_NASporttype", m[19]);
set_field("nps_connectinfo", m[20]);
set_field("nps_framedprotocol", m[21]);
set_field("nps_servicetype", m[22]);
set_field("nps_authenticationtype", m[23]);
set_field("nps_policyname", m[24]);
set_field("nps_reasoncode", m[25]);
set_field("nps_class", m[26]);
set_field("nps_sessiontimeout", m[27]);
set_field("nps_idletimeout", m[28]);
set_field("nps_terminationaction", m[29]);
set_field("nps_EAPfriendlyname", m[30]);
set_field("nps_acctstatustype", m[31]);
set_field("nps_acctdelaytime", m[32]);
set_field("nps_acctinputoctets", m[33]);
set_field("nps_acctoutputoctets", m[34]);
set_field("nps_acctsessionID", m[35]);
set_field("nps_acctauthentic", m[36]);
set_field("nps_acctsessiontime", m[37]);
set_field("nps_acctinputpackets", m[38]);
set_field("nps_acctoutputpackets", m[39]);
set_field("nps_acctterminatecause", m[40]);
set_field("nps_acctmultissnID", m[41]);
set_field("nps_acctlinkcount", m[42]);
set_field("nps_acctinteriminterval", m[43]);
set_field("nps_tunneltype", m[44]);
set_field("nps_tunnelmediumtype", m[45]);
set_field("nps_tunnelclientendpt", m[46]);
set_field("nps_tunnelserverendpt", m[47]);
set_field("nps_accttunnelconn", m[48]);
set_field("nps_tunnelpvtgroupID", m[49]);
set_field("nps_tunnelassignmentID", m[50]);
set_field("nps_tunnelpreference", m[51]);
set_field("nps_MSacctauthtype", m[52]);
set_field("nps_MSacctEAPtype", m[53]);
set_field("nps_MSRASversion", m[54]);
set_field("nps_MSRASVendor", m[55]);
set_field("nps_MSCHAPerror", m[56]);
set_field("nps_MSCHAPdomain", m[57]);
set_field("nps_MSMPPEencryptiontypes", m[58]);
set_field("nps_MSMPPEencryptionpolicy", m[59]);
set_field("nps_proxypolicyname", m[60]);
set_field("nps_providertype", m[61]);
set_field("nps_providername", m[62]);
set_field("nps_remoteserveraddress", m[63]);
set_field("nps_MSRASclientname", m[64]);
set_field("nps_MSRASclientversion", m[65]);
end
Save the rule, create a new pipeline under manage pipelines/add new pipeline, connect the stream that contains the NPS logfiles with “edit connection”, edit Stage 0 and add your MS NPS Logfile Parser.