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.
Table of Contents
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;
Based on the Extracted fields, you can create visualization dashboards. Below are sample dashboards;
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, :).
Related Tutorials
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