1
0
mirror of https://github.com/mail-in-a-box/mailinabox.git synced 2025-08-07 04:50:54 +00:00

Web statistics using goaccess.

This commit is contained in:
kirk 2025-07-21 06:44:17 -04:00
parent 3cde9a8893
commit e922401298
8 changed files with 134 additions and 3 deletions

View File

@ -3,5 +3,5 @@
before = common.conf before = common.conf
[Definition] [Definition]
failregex=<HOST> - .*GET /admin/munin/.* HTTP/\d+\.\d+\" 401.* failregex=^.+?:\d+ <HOST> - .*GET /admin/munin/.* HTTP/\d+\.\d+\" 401.*
ignoreregex = ignoreregex =

2
conf/goaccess_persist Normal file
View File

@ -0,0 +1,2 @@
#!/usr/bin/env /bin/bash
/usr/bin/goaccess --process-and-exit

View File

@ -10,3 +10,10 @@ upstream php-fpm {
server unix:/var/run/php/php8.0-fpm.sock; server unix:/var/run/php/php8.0-fpm.sock;
} }
# Reconfigure access log to include vhost to match goaccess VCOMBINED.
# Cancel default logging, re-enabled in servers.
access_log off;
# Log format to match goaccess.
log_format vcombined '$host:$server_port $remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';

View File

@ -10,6 +10,8 @@ server {
server_name $HOSTNAME; server_name $HOSTNAME;
root /tmp/invalid-path-nothing-here; root /tmp/invalid-path-nothing-here;
access_log /var/log/nginx/access.log vcombined;
# Improve privacy: Hide version an OS information on # Improve privacy: Hide version an OS information on
# error pages and in the "Server" HTTP-Header. # error pages and in the "Server" HTTP-Header.
server_tokens off; server_tokens off;
@ -36,6 +38,8 @@ server {
server_name $HOSTNAME; server_name $HOSTNAME;
access_log /var/log/nginx/access.log vcombined;
# Improve privacy: Hide version an OS information on # Improve privacy: Hide version an OS information on
# error pages and in the "Server" HTTP-Header. # error pages and in the "Server" HTTP-Header.
server_tokens off; server_tokens off;

View File

@ -15,6 +15,11 @@ if [ "$(date "+%u")" -eq 1 ]; then
management/mail_log.py -t week | management/email_administrator.py "Mail-in-a-Box Usage Report" management/mail_log.py -t week | management/email_administrator.py "Mail-in-a-Box Usage Report"
fi fi
# On Mondays, i.e. once a week, send the administrator a web analytics report.
if [ "$(date "+%u")" -eq 1 ]; then
goaccess -o html | management/email_administrator_attachment.py "MIAB Web Analytics Report" "Mail-in-a-Box Web analytics report is attached." "webstats.html"
fi
# Take a backup. # Take a backup.
management/backup.py 2>&1 | management/email_administrator.py "Backup Status" management/backup.py 2>&1 | management/email_administrator.py "Backup Status"

View File

@ -0,0 +1,73 @@
#!/usr/local/lib/mailinabox/env/bin/python
# Reads in STDIN. If the stream is not empty, mail it to the system administrator.
import sys
import html
import smtplib
import email
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
# In Python 3.6:
#from email.message import Message
from utils import load_environment
# Load system environment info.
env = load_environment()
# Process command line args.
subject = sys.argv[1] or 'MIAB Administration'
body = sys.argv[2] or 'Please see the attachment. --Mail-in-a-Box'
attachmentname = sys.argv[3] or 'attachment.html'
# Administrator's email address.
admin_addr = "administrator@" + env['PRIMARY_HOSTNAME']
# Read in STDIN.
attachment = sys.stdin.read().strip()
# If there's nothing coming in, just exit.
if attachment == "":
sys.exit(0)
# create MIME message
msg = MIMEMultipart('alternative')
# In Python 3.6:
#msg = Message()
msg['From'] = '"{}" <{}>'.format(env['PRIMARY_HOSTNAME'], admin_addr)
msg['To'] = admin_addr
msg['Subject'] = "[{}] {}".format(env['PRIMARY_HOSTNAME'], subject)
body_html = f'<html><body><pre style="overflow-x: scroll; white-space: pre;">{html.escape(body)}</pre></body></html>'
msg.attach(MIMEText(body, 'plain'))
msg.attach(MIMEText(body_html, 'html'))
# Attach content as file
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment);
encoders.encode_base64(part);
part.add_header('Content-Disposition', f"attachment; filename={attachmentname}")
msg.attach(part);
# In Python 3.6:
#msg.set_content(content)
#msg.add_alternative(content_html, "html")
# send
smtpclient = smtplib.SMTP('127.0.0.1', 25)
smtpclient.ehlo()
smtpclient.sendmail(
admin_addr, # MAIL FROM
admin_addr, # RCPT TO
msg.as_string())
smtpclient.quit()

View File

@ -19,7 +19,7 @@ fi
echo "Installing Nginx (web server)..." echo "Installing Nginx (web server)..."
apt_install nginx php"${PHP_VER}"-cli php"${PHP_VER}"-fpm idn2 apt_install nginx php"${PHP_VER}"-cli php"${PHP_VER}"-fpm idn2 goaccess
rm -f /etc/nginx/sites-enabled/default rm -f /etc/nginx/sites-enabled/default
@ -145,6 +145,46 @@ if [ ! -f "$STORAGE_ROOT/www/default/index.html" ]; then
fi fi
chown -R "$STORAGE_USER" "$STORAGE_ROOT/www" chown -R "$STORAGE_USER" "$STORAGE_ROOT/www"
echo "Setting up goaccess web analytics..."
# Set default configuration for goaccess web stats.
mkdir -p "/var/lib/mailinabox/goaccess_db"
tools/editconf.py /etc/goaccess/goaccess.conf -c '#' -s \
persist=true \
restore=true \
keep-last=7 \
db-path=/var/lib/mailinabox/goaccess_db \
html-report-title=Mailinabox \
log-file=/var/log/nginx/access.log \
log-format=VCOMBINED
# Create a pre-rotate action to preserve log info.
PREROT="/etc/logrotate.d/httpd-prerotate"
if [ -d "$PREROT" ] ; then
NOPREROT=1; # false, there is a prerotate
else
NOPREROT=0; # true, there is no prerotate
fi
mkdir -p "$PREROT"
# If the prerotate doesn't exist, configure.
if [ "$NOPREROT" -eq 0 ]; then
echo "- Configuring log prerotate action."
chown root:root "$PREROT"
chmod 755 "$PREROT";
else # There is a prerotate, no change.
echo "- No change to $PREROT";
fi
# Create action.
cp conf/goaccess_persist "$PREROT"
chown root:root "$PREROT/goaccess_persist"
chmod a+x "$PREROT/goaccess_persist"
# Start services. # Start services.
restart_service nginx restart_service nginx
restart_service php"$PHP_VER"-fpm restart_service php"$PHP_VER"-fpm

View File

@ -23,7 +23,7 @@ for fn in glob.glob("/var/log/nginx/access.log*"):
# Find lines that are GETs on the bootstrap script by either curl or wget. # Find lines that are GETs on the bootstrap script by either curl or wget.
# (Note that we purposely skip ...?ping=1 requests which is the admin panel querying us for updates.) # (Note that we purposely skip ...?ping=1 requests which is the admin panel querying us for updates.)
# (Also, the URL changed in January 2016, but we'll accept both.) # (Also, the URL changed in January 2016, but we'll accept both.)
m = re.match(rb"(?P<ip>\S+) - - \[(?P<date>.*?)\] \"GET /(bootstrap.sh|setup.sh) HTTP/.*\" 200 \d+ .* \"(?:curl|wget)", line, re.I) m = re.match(rb"(?P<hostport>\S+) (?P<ip>\S+) - - \[(?P<date>.*?)\] \"GET /(bootstrap.sh|setup.sh) HTTP/.*\" 200 \d+ .* \"(?:curl|wget)", line, re.I)
if m: if m:
date, time = m.group("date").decode("ascii").split(":", 1) date, time = m.group("date").decode("ascii").split(":", 1)
date = dateutil.parser.parse(date).date().isoformat() date = dateutil.parser.parse(date).date().isoformat()