Visualize WordPress User Activity Logs on ELK Stack

|
Last Updated:
|
|

In this tutorial, you will learn how to visualize WordPress user activity logs on ELK stack. WordPress do not provide an easy way to have an overview of user activity or log any user activity on a server log file. Logging is paramount in detecting, preventing or minimizing the impact of any security breach. There are a thousand various WordPress plugins that have been developed to enable WordPress logging. Some of these plugins provides the ability to log any WordPress user activity logs on the local system log files for easy analysis. In this setup however, we use Sucuri WordPress plugin to enable WordPress logging to a local system file which we will then read with Filebeat and process it with Logstash before sending the data to Elasticsearch for indexing and later visualize on Kibana interface.

Are you using WordPress and looking for a professional WordPress website builder? Look no further since Elementor can help you create beautiful pages.

Monitoring WordPress User Activity Logs on ELK Stack

Install and Setup WordPress

Of course you must be having an already running WordPress if you are here. However, you might as well want to check the links below on how to install and setup WordPress site;

Install latest WordPress with LAMP Stack on Ubuntu 20.04

Install WordPress with Nginx and MySQL 8 on CentOS 8

Install WordPress 5 with Nginx on Debian 10 Buster

Install WordPress Security Auditing/File Integrity Monitoring Plugin

As stated above, there are a thousand plugins that can be used to audit and record every WordPress user activity. You can use any plugin of your reference. But just so we can be on the same page, have used the Sucuri plugin in this setup.

We cannot dive deeper into the installation and setup of the Sucuri plugin. You can visit the Sucuri plugin installation page for that.

Now assuming you have installed and activated your plugin, create a local system log file directory where to write the WordPress audit events to.

We use, /var/log/wordpress/, in this setup. Might be different in your case.

mkdir /var/log/wordpress/

Set the proper ownership of the logging directory. For example, set the user and group to www-data or nginx depending on the HTTP server you are using.

chown -R www-data: /var/log/wordpress

Next, navigate to the Sucuri Security > Settings > General Settings > Log Exporter and enter the full path to your WordPress audit logging file, in this setup we use /var/log/wordpress/kifarunix-demo.com.log.

Once you have entered entered the path, click Submit to save and create the log file.

From now henceforth, any WordPress activity is logged to /var/log/wordpress/kifarunix-demo.com.log.

Just to demonstrate how Sucuri does the audit logging;

tail -f /var/log/wordpress/kifarunix-demo.com.log

Successful and failed login attempt to the WordPress site;


2020-11-12 04:15:10 WordPressAudit kifarunix-demo.com [email protected] : Error: 192.168.57.1; User authentication failed: demouser
2020-11-12 04:15:24 WordPressAudit kifarunix-demo.com [email protected] : Notice: 192.168.57.1; User authentication succeeded: gentoo

WordPress plugins activation and deactivation logs;


2020-11-12 04:17:04 WordPressAudit kifarunix-demo.com [email protected] : Warning: gentoo, 192.168.57.1; Plugin activated: Hello Dolly (v1.7.2; hello.php)
2020-11-12 04:17:13 WordPressAudit kifarunix-demo.com [email protected] : Warning: gentoo, 192.168.57.1; Plugin deactivated: Hello Dolly (v1.7.2; hello.php)

WordPress Blogs Posts Management logs;

New draft post/page;


2020-11-12 04:30:02 WordPressAudit kifarunix-demo.com [email protected] : Notice: gentoo, 192.168.57.1; Post status has been changed; details: ID: 5,Old status: auto-draft,New status: draft,Title: My new post
2020-11-12 04:45:53 WordPressAudit kifarunix-demo.com [email protected] : Notice: gentoo, 192.168.57.1; Page status has been changed; details: ID: 9,Old status: auto-draft,New status: draft,Title: sample page

Updating Existing draft post/page;


2020-11-12 04:31:22 WordPressAudit kifarunix-demo.com [email protected] : Notice: gentoo, 192.168.57.1; Revision status has been changed; details: ID: 7,Old status: new,New status: inherit,Title: My new post
2020-11-12 04:47:16 WordPressAudit kifarunix-demo.com [email protected] : Notice: gentoo, 192.168.57.1; Revision status has been changed; details: ID: 11,Old status: new,New status: inherit,Title: sample page

Publish a blog post/page;


2020-11-12 04:32:49 WordPressAudit kifarunix-demo.com [email protected] : Notice: gentoo, 192.168.57.1; Post status has been changed; details: ID: 5,Old status: draft,New status: publish,Title: My new post
2020-11-12 04:32:49 WordPressAudit kifarunix-demo.com [email protected] : Notice: gentoo, 192.168.57.1; Post was created; ID: 5; name: My new post
2020-11-12 04:47:49 WordPressAudit kifarunix-demo.com [email protected] : Notice: gentoo, 192.168.57.1; Page status has been changed; details: ID: 9,Old status: draft,New status: publish,Title: sample page
2020-11-12 04:47:49 WordPressAudit kifarunix-demo.com [email protected] : Notice: gentoo, 192.168.57.1; Page was created; ID: 9; name: sample page

Delete published blog post;


2020-11-12 04:33:54 WordPressAudit kifarunix-demo.com [email protected] : Notice: gentoo, 192.168.57.1; Post status has been changed; details: ID: 5,Old status: publish,New status: trash,Title: My new post
2020-11-12 04:48:24 WordPressAudit kifarunix-demo.com [email protected] : Notice: gentoo, 192.168.57.1; Page status has been changed; details: ID: 9,Old status: publish,New status: trash,Title: sample page

Restore trashed post;

2020-11-12 04:35:04 WordPressAudit kifarunix-demo.com [email protected] : Notice: gentoo, 192.168.57.1; Post status has been changed; details: ID: 5,Old status: trash,New status: publish,Title: My new post

Draft published blog post;

2020-11-12 04:36:19 WordPressAudit kifarunix-demo.com [email protected] : Notice: gentoo, 192.168.57.1; Post status has been changed; details: ID: 5,Old status: publish,New status: draft,Title: My new post

User Account Creation;

2020-11-12 04:38:22 WordPressAudit kifarunix-demo.com [email protected] : Warning: gentoo, 192.168.57.1; User account created; ID: 2; name: demouser; email: [email protected]; roles: editor

User account changes;

2020-11-12 04:39:52 WordPressAudit kifarunix-demo.com [email protected] : Warning: gentoo, 192.168.57.1; User account edited; ID: 2; name: demouser; old_name: demouser; email: [email protected]; old_email: [email protected]; roles: editor; old_roles: editor

User Account deletion;

2020-11-12 04:41:44 WordPressAudit kifarunix-demo.com [email protected] : Warning: gentoo, 192.168.57.1; User account deleted; ID: 2

File uploads;

2020-11-12 04:43:32 WordPressAudit kifarunix-demo.com [email protected] : Notice: gentoo, 192.168.57.1; Media file added; ID: 8; name: linuxtux; type: image/jpeg

File deletion;

2020-11-12 04:44:23 WordPressAudit kifarunix-demo.com [email protected] : Warning: gentoo, 192.168.57.1; Post deleted: (multiple entries): Post id: 8

Theme Activation;

2020-11-12 04:49:31 WordPressAudit kifarunix-demo.com [email protected] : Warning: gentoo, 192.168.57.1; Theme activated: Twenty Nineteen

Widget changes;

2020-11-12 04:50:37 WordPressAudit kifarunix-demo.com [email protected] : Warning: gentoo, 192.168.57.1; Widget recent-posts (recent-posts-3) added to sidebar-2 (#2; size 250x200)
2020-11-12 04:51:39 WordPressAudit kifarunix-demo.com [email protected] : Warning: gentoo, 192.168.57.1; Widget recent-posts (recent-posts-4) deleted from sidebar-2 (#2; size 250x200)

And the list goes on.

Monitor WordPress User Activity Logs on ELK Stack

In this setup, we will collect the logs using Filebeat and ship them to Logstash where we will further process to extract specific log fields after which they are send to Elasticsearch for storage and indexing and hence visualization on Kibana.

Follow the links below to install and setup Filebeat as well as ELK stack;

Install and Setup ELK Stack

Install ELK Stack on Ubuntu 20.04

Installing ELK Stack on CentOS 8

Configure Logstash to Process WordPress User Activity Logs

Assuming you already setup ELK stack, you need to configure Logstash to receive the WordPress user activity logs and processes them.

 Logstash data processing pipeline has three sections;

  • INPUT: input section is used to ingest data from different endpoints into Logstash. We use Filebeat in this setup.
  • FILTERS: which processes and transform the data received. We use grok patterns to extract the fields from the WordPress User activity logs.
  • OUTPUT: which stashes processed data into a specified destination, which can be Elasticsearch. We use Elasticsearch in this guide.

You can read more about Logstash Pipeline here.

Below is our Logstash configuration file with the Filebeat input, grok filters to process the WordPress log activity and Elasticsearch output defined.

vim /etc/logstash/conf.d/wordpress.conf

input {
  beats {
    port => 5044
  }
}
filter {
	# Extract Authentication Logs
	grok { 
		match => { "message" => "(?<event_time>%{YEAR}-%{MONTHNUM}-%{MONTHDAY}\s%{TIME})\s\w+\s%{HOSTNAME:host_name}\s.*\s:\s(?<log_level>\w+):\s%{IPORHOST:src_ip};\s(?<msg>.*):\s(?<user_name>\w+)" }
		add_tag => "authentication"
	}
	# plugins activation and deactivation logs;
	grok { 
		match => { "message" => "(?<event_time>%{YEAR}-%{MONTHNUM}-%{MONTHDAY}\s%{TIME})\s\w+\s%{HOSTNAME:host_name}\s.*\s:\s(?<log_level>\w+):\s(?<user_name>\w+),\s%{IPORHOST:src_ip};\s(?<msg>Plugin.*):\s(?<plugin>.*)" }
		add_tag => "plugins"
	}
	# Blogs Posts Management
	## New draft post/page, publish post/pages, delete/restore posts/pages, 
	grok {
		match => { "message" => "(?<event_time>%{YEAR}-%{MONTHNUM}-%{MONTHDAY}\s%{TIME})\s\w+\s%{HOSTNAME:host_name}\s.*\s:\s(?<log_level>\w+):\s(?<user_name>\w+),\s%{IPORHOST:src_ip};\s(?<msg>Post.*|Page.*);\s.*Old\sstatus:\s(?<old_status>\w.+),New\sstatus:\s(?<new_status>\w.+),Title:\s(?<title>.*)" }
		add_tag => "posts_pages"
	}
	## Updating Existing draft post/page;
	grok {
		match => { "message" => "(?<event_time>%{YEAR}-%{MONTHNUM}-%{MONTHDAY}\s%{TIME})\s\w+\s%{HOSTNAME:host_name}\s.*\s:\s(?<log_level>\w+):\s(?<user_name>\w+),\s%{IPORHOST:src_ip};\s(?<msg>Revision.*);\s.*Old\sstatus:\s(?<old_status>\w.+),New\sstatus:\s(?<new_status>\w.+),Title:\s(?<title>.*)" }
		add_tag => "posts_pages"
	}
	## User Account Created
	grok {
		match => { "message" => "(?<event_time>%{YEAR}-%{MONTHNUM}-%{MONTHDAY}\s%{TIME})\s\w+\s%{HOSTNAME:host_name}\s.*\s:\s(?<log_level>\w+):\s(?<created_by>\w+),\s%{IPORHOST:src_ip};\s(?<msg>User account created);.*name:\s(?<user_name>\w+);\semail:\s(?<email_address>[a-zA-Z0-9_.+=:-]+@[0-9A-Za-z][0-9A-Za-z-]{0,62}(?:\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})));\sroles:\s(?<user_role>\w.+)" }
		add_tag => "account_created"
	}
	## User Account Changes
	grok {
		match => { "message" => "(?<event_time>%{YEAR}-%{MONTHNUM}-%{MONTHDAY}\s%{TIME})\s\w+\s%{HOSTNAME:host_name}\s.*\s:\s(?<log_level>\w+):\s(?<edited_by>\w+),\s%{IPORHOST:src_ip};\s(?<msg>User account edited);.*name:\s(?<user_name>\w+);\sold_name:\s(?<old_name>\w+);\semail:\s(?<email_address>[a-zA-Z0-9_.+=:-]+@[0-9A-Za-z][0-9A-Za-z-]{0,62}(?:\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})));\sold_email:\s(?<old_email_address>[a-zA-Z0-9_.+=:-]+@[0-9A-Za-z][0-9A-Za-z-]{0,62}(?:\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})));\sroles:\s(?<user_role>\w.+);\sold_roles:\s(?<old_role>\w.+)" } 
		add_tag => "account_edited"
	}
	## User account deletion
	grok {
		match => { "message" => "(?<event_time>%{YEAR}-%{MONTHNUM}-%{MONTHDAY}\s%{TIME})\s\w+\s%{HOSTNAME:host_name}\s.*\s:\s(?<log_level>\w+):\s(?<deleted_by>\w+),\s%{IPORHOST:src_ip};\s(?<msg>User account deleted);\sID:\s(?<deleted_user_id>\d+)" }
		add_tag => "account_deleted"
	}
	## File Uploads
	grok {
		match => { "message" => "(?<event_time>%{YEAR}-%{MONTHNUM}-%{MONTHDAY}\s%{TIME})\s\w+\s%{HOSTNAME:host_name}\s.*\s:\s(?<log_level>\w+):\s(?<user_name>\w+),\s%{IPORHOST:src_ip};\s(?<msg>Media file added);\sID:\s(?<file_id>\d+);\sname:\s(?<file_name>\w+);\stype:\s(?<file_type>\w.+)" }
		add_tag => "file_added"
	}
	## File Deletion
	grok {
		match => { "message" => "(?<event_time>%{YEAR}-%{MONTHNUM}-%{MONTHDAY}\s%{TIME})\s\w+\s%{HOSTNAME:host_name}\s.*\s:\s(?<log_level>\w+):\s(?<user_name>\w+),\s%{IPORHOST:src_ip};\s(?<msg>Post deleted).*Post\sid:\s(?<file_id>\d+)" }
		add_tag => "file_deleted"
	}
	## Theme Activations
	grok {
		match => { "message" => "(?<event_time>%{YEAR}-%{MONTHNUM}-%{MONTHDAY}\s%{TIME})\s\w+\s%{HOSTNAME:host_name}\s.*\s:\s(?<log_level>\w+):\s(?<user_name>\w+),\s%{IPORHOST:src_ip};\s(?<msg>Theme.*):\s(?<theme_name>\w.+)" }
		add_tag => "theme_changes"
	}
	## Widget management
	grok {
		match => { "message" => "(?<event_time>%{YEAR}-%{MONTHNUM}-%{MONTHDAY}\s%{TIME})\s\w+\s%{HOSTNAME:host_name}\s.*\s:\s(?<log_level>\w+):\s(?<user_name>\w+),\s%{IPORHOST:src_ip};\s(?<msg>Widget.*)" }
		add_tag => "widget_changes"
	}
}
output {
   elasticsearch {
     hosts => ["192.168.57.30:9200"]
     index => "wordpress-%{+YYYY.MM.dd}"
   }
  #stdout { codec => rubydebug }

Feel free to adjust the grok patterns to suit your needs. You can utilize the Kibana Grok Debugger (Kibana > Dev Tools > Grok Debugger) or Herokuapp Grok Debugger to create your grok patterns.

If you need to debug Logstash Grok Filters to confirm that they can actually parse your logs into the required fields, see the link below on how to debug Logstash Grok filters.

How to Debug Logstash Grok Filters

Test Logstash Configuration

Once you are done with configurations, run the command below to verify the Logstash configuration before you can start it.

/usr/share/logstash/bin/logstash --path.settings /etc/logstash -t

Sending Logstash logs to /var/log/logstash which is now configured via log4j2.properties
[2020-11-13T19:58:51,616][INFO ][org.reflections.Reflections] Reflections took 76 ms to scan 1 urls, producing 21 keys and 41 values 
Configuration OK
[2020-11-13T19:58:54,815][INFO ][logstash.runner          ] Using config.test_and_exit mode. Config Validation Result: OK. Exiting Logstash

Well, if you get Configuration OK then you are good to go.

Running Logstash

Now, start and enable logstash to run on system boot;

systemctl enable --now logstash

Check the status;

systemctl status logstash

Verify the port is opened;

ss -antlp | grep :5044
LISTEN 0 128 *:5044 *:* users:(("java",pid=3273,fd=99))

Open the port on firewall to allow remove beats to connect to it;

On RHEL based derivatives;

firewall-cmd --add-port=5044/tcp --permanent
firewall-cmd --reload

On Debian based derivatives;

ufw allow 5044/tcp

On IPtables;

iptables -A INPUT -p tcp --dport 5044 -j ACCEPT

Install and Setup Filebeat

Install and Configure Filebeat on CentOS 8

Install Filebeat on Fedora 30/Fedora 29/CentOS 7

Install and Configure Filebeat 7 on Ubuntu 18.04/Debian 9.8

Configure Filebeat

Assuming you have already installed Filebeat and is running on a server running WordPress, you can configure it to read the WordPress user activity logs as follows. In this setup, the plugin has been configure to write WordPress user activity logs to the local file, /var/log/wordpress/kifarunix-demo.com.log.

Open Filebeat configuration for editing;

vim /etc/filebeat/filebeat.yml

Enable the Filebeat input type log and configure it to read the WordPress user activity log file;

# ============================== Filebeat inputs ===============================

filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /var/log/wordpress/kifarunix-demo.com.log
...

Configure Filebeat to sent logs to Logstash instead of Elasticsearch.


...
# ================================== Outputs ===================================

# Configure what output to use when sending the data collected by the beat.

# ---------------------------- Elasticsearch Output ----------------------------
#output.elasticsearch:
  # Array of hosts to connect to.
  #hosts: ["localhost:9200"]
...
# ------------------------------ Logstash Output -------------------------------
output.logstash:
  # The Logstash hosts
  hosts: ["192.168.57.30:5044"]
...

Save and exit the configuration file.

Ensure that you can connect to Logstash Port 5044/tcp;

telnet 192.168.57.30 5044
Trying 192.168.57.30...
Connected to 192.168.57.30.
Escape character is '^]'.
Verify Filebeat Configuration

Run Filebeat in foreground to redirect the output to standard error instead so as to check if it connects to Logstash successfully or not before you can start it.

filebeat -e

...
2020-11-13T17:36:08.735Z	INFO	log/input.go:157	Configured paths: [/var/log/wordpress/kifarunix-demo.com.log]
2020-11-13T17:36:08.736Z	INFO	[crawler]	beater/crawler.go:141	Starting input (ID: 13913356589683053536)
2020-11-13T17:36:08.737Z	INFO	[crawler]	beater/crawler.go:108	Loading and starting Inputs completed. Enabled inputs: 1
2020-11-13T17:36:08.738Z	INFO	cfgfile/reload.go:164	Config reloader started
2020-11-13T17:36:08.739Z	INFO	cfgfile/reload.go:224	Loading of config files completed.
2020-11-13T17:36:08.738Z	INFO	log/harvester.go:302	Harvester started for file: /var/log/wordpress/kifarunix-demo.com.log
2020-11-13T17:36:11.648Z	INFO	[add_cloud_metadata]	add_cloud_metadata/add_cloud_metadata.go:89	add_cloud_metadata: hosting provider type not detected.
2020-11-13T17:36:12.649Z	INFO	[publisher_pipeline_output]	pipeline/output.go:143	Connecting to backoff(async(tcp://192.168.57.30:5044))
2020-11-13T17:36:12.651Z	INFO	[publisher]	pipeline/retry.go:219	retryer: send unwait signal to consumer
2020-11-13T17:36:12.654Z	INFO	[publisher]	pipeline/retry.go:223	  done
2020-11-13T17:36:12.653Z	INFO	[publisher_pipeline_output]	pipeline/output.go:151	Connection to backoff(async(tcp://192.168.57.30:5044)) established

If you see such a line as Connection to backoff(async(tcp://192.168.57.30:5044)) established, then all if fine.

Proceed to start and enable filebeat to run on system boot;

systemctl start filebeat

Monitoring WordPress User Activity Logs on ELK Stack

Next, verify the Elasticsearch data reception from Logstash and create Kibana index to enable you visualize user activity.

You can check this guide on how to create Kibana index;

Create Kibana Index to Visualize Event Data

Next, perform some WordPress site activities and you should be able see events populated on Kibana;

Visualize WordPress User Activity Logs on ELK Stack

Based on the Extracted fields, you can create visualization dashboards. Below are sample dashboards;

user authentications
account activity
posts pages activity

Those are just but a sample dashboards we were able to make in this guide in regards to WordPress user activity based on the logs generated by the Sucuri plugin. You can do more, :).

Restrict Access to WordPress Login Page to Specific IPs with libModSecurity

How to fix WordPress could not establish a secure connection to WordPress.org

Process and Visualize ModSecurity Logs on ELK Stack

Deploy a Single Node Elastic Stack Cluster on Docker Containers

Create Kibana Visualization Dashboards for ModSecurity Logs

SUPPORT US VIA A VIRTUAL CUP OF COFFEE

We're passionate about sharing our knowledge and experiences with you through our blog. If you appreciate our efforts, consider buying us a virtual coffee. Your support keeps us motivated and enables us to continually improve, ensuring that we can provide you with the best content possible. Thank you for being a coffee-fueled champion of our work!

Photo of author
gen_too
Co-founder of Kifarunix.com, Linux Tips and Tutorials. Linux/Unix admin and author at Kifarunix.com.

Leave a Comment