mirror of
https://github.com/Mailu/Mailu.git
synced 2025-05-15 22:16:39 +02:00
Fixed log filter not filtering out log messages for dovecot/nginx/postfix.
Fixed postfix not logging to standard out. Fixed not all containers logging to journald. Removed POSTFIX_LOG_FILE functionality. Added documentation on how to achieve the same (log to file) via journald & rsyslogd (see new FAQ entry 'How can I view and export the logs of a Mailu container?').
This commit is contained in:
parent
efcf7a1581
commit
60b9ff0090
@ -6,6 +6,8 @@ import re
|
|||||||
from pwd import getpwnam
|
from pwd import getpwnam
|
||||||
import socket
|
import socket
|
||||||
import tenacity
|
import tenacity
|
||||||
|
import subprocess
|
||||||
|
import threading
|
||||||
|
|
||||||
@tenacity.retry(stop=tenacity.stop_after_attempt(100),
|
@tenacity.retry(stop=tenacity.stop_after_attempt(100),
|
||||||
wait=tenacity.wait_random(min=2, max=5))
|
wait=tenacity.wait_random(min=2, max=5))
|
||||||
@ -27,7 +29,7 @@ def _coerce_value(value):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
class LogFilter(object):
|
class LogFilter(object):
|
||||||
def __init__(self, stream, re_patterns, log_file):
|
def __init__(self, stream, re_patterns):
|
||||||
self.stream = stream
|
self.stream = stream
|
||||||
if isinstance(re_patterns, list):
|
if isinstance(re_patterns, list):
|
||||||
self.pattern = re.compile('|'.join([f'(?:{pattern})' for pattern in re_patterns]))
|
self.pattern = re.compile('|'.join([f'(?:{pattern})' for pattern in re_patterns]))
|
||||||
@ -36,7 +38,6 @@ class LogFilter(object):
|
|||||||
else:
|
else:
|
||||||
self.pattern = re_patterns
|
self.pattern = re_patterns
|
||||||
self.found = False
|
self.found = False
|
||||||
self.log_file = log_file
|
|
||||||
|
|
||||||
def __getattr__(self, attr_name):
|
def __getattr__(self, attr_name):
|
||||||
return getattr(self.stream, attr_name)
|
return getattr(self.stream, attr_name)
|
||||||
@ -48,12 +49,6 @@ class LogFilter(object):
|
|||||||
if not self.pattern.search(data):
|
if not self.pattern.search(data):
|
||||||
self.stream.write(data)
|
self.stream.write(data)
|
||||||
self.stream.flush()
|
self.stream.flush()
|
||||||
if self.log_file:
|
|
||||||
try:
|
|
||||||
with open(self.log_file, 'a', encoding='utf-8') as l:
|
|
||||||
l.write(data)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
else:
|
else:
|
||||||
# caught bad pattern
|
# caught bad pattern
|
||||||
self.found = True
|
self.found = True
|
||||||
@ -74,10 +69,10 @@ def _is_compatible_with_hardened_malloc():
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def set_env(required_secrets=[], log_filters=[], log_file=None):
|
def set_env(required_secrets=[], log_filters=[]):
|
||||||
if log_filters:
|
if log_filters:
|
||||||
sys.stdout = LogFilter(sys.stdout, log_filters, log_file)
|
sys.stdout = LogFilter(sys.stdout, log_filters)
|
||||||
sys.stderr = LogFilter(sys.stderr, log_filters, log_file)
|
sys.stderr = LogFilter(sys.stderr, log_filters)
|
||||||
log.basicConfig(stream=sys.stderr, level=os.environ.get("LOG_LEVEL", 'WARNING'))
|
log.basicConfig(stream=sys.stderr, level=os.environ.get("LOG_LEVEL", 'WARNING'))
|
||||||
|
|
||||||
if not 'LD_PRELOAD' in os.environ and _is_compatible_with_hardened_malloc():
|
if not 'LD_PRELOAD' in os.environ and _is_compatible_with_hardened_malloc():
|
||||||
@ -115,3 +110,24 @@ def drop_privs_to(username='mailu'):
|
|||||||
os.setgid(pwnam.pw_gid)
|
os.setgid(pwnam.pw_gid)
|
||||||
os.setuid(pwnam.pw_uid)
|
os.setuid(pwnam.pw_uid)
|
||||||
os.environ['HOME'] = pwnam.pw_dir
|
os.environ['HOME'] = pwnam.pw_dir
|
||||||
|
|
||||||
|
# forwards text lines from src to dst in an infinite loop
|
||||||
|
def forward_text_lines(src, dst):
|
||||||
|
while True:
|
||||||
|
current_line = src.readline()
|
||||||
|
dst.write(current_line)
|
||||||
|
|
||||||
|
|
||||||
|
# runs a process and passes its standard/error output to the standard/error output of the current python script
|
||||||
|
def run_process_and_forward_output(cmd):
|
||||||
|
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
|
||||||
|
stdout_thread = threading.Thread(target=forward_text_lines, args=(process.stdout, sys.stdout))
|
||||||
|
stdout_thread.daemon = True
|
||||||
|
stdout_thread.start()
|
||||||
|
|
||||||
|
stderr_thread = threading.Thread(target=forward_text_lines, args=(process.stderr, sys.stderr))
|
||||||
|
stderr_thread.daemon = True
|
||||||
|
stderr_thread.start()
|
||||||
|
|
||||||
|
process.wait()
|
||||||
|
@ -3,13 +3,11 @@
|
|||||||
import os
|
import os
|
||||||
import glob
|
import glob
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import logging as log
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from podop import run_server
|
from podop import run_server
|
||||||
from socrate import system, conf
|
from socrate import system, conf
|
||||||
|
|
||||||
system.set_env(log_filters=r'Error\: SSL context initialization failed, disabling SSL\: Can\'t load SSL certificate \(ssl_cert setting\)\: The certificate is empty$')
|
system.set_env(log_filters=[r'Error\: SSL context initialization failed, disabling SSL\: Can\'t load SSL certificate \(ssl_cert setting\)\: The certificate is empty$'])
|
||||||
|
|
||||||
def start_podop():
|
def start_podop():
|
||||||
system.drop_privs_to('mail')
|
system.drop_privs_to('mail')
|
||||||
@ -35,4 +33,5 @@ os.system("chown mail:mail /mail")
|
|||||||
os.system("chown -R mail:mail /var/lib/dovecot /conf")
|
os.system("chown -R mail:mail /var/lib/dovecot /conf")
|
||||||
|
|
||||||
multiprocessing.Process(target=start_podop).start()
|
multiprocessing.Process(target=start_podop).start()
|
||||||
os.system("dovecot -c /etc/dovecot/dovecot.conf -F")
|
cmd = ['/usr/sbin/dovecot', '-c', '/etc/dovecot/dovecot.conf', '-F']
|
||||||
|
system.run_process_and_forward_output(cmd)
|
||||||
|
@ -4,7 +4,7 @@ import os
|
|||||||
import subprocess
|
import subprocess
|
||||||
from socrate import system
|
from socrate import system
|
||||||
|
|
||||||
system.set_env(log_filters=r'could not be resolved \(\d\: [^\)]+\) while in resolving client address, client\: [^,]+, server: [^\:]+\:(25,110,143,587,465,993,995)$')
|
system.set_env(log_filters=r'could not be resolved \(\d\: [^\)]+\) while in resolving client address, client\: [^,]+, server: [^\:]+\:(25|110|143|587|465|993|995)$')
|
||||||
|
|
||||||
# Check if a stale pid file exists
|
# Check if a stale pid file exists
|
||||||
if os.path.exists("/var/run/nginx.pid"):
|
if os.path.exists("/var/run/nginx.pid"):
|
||||||
@ -17,4 +17,5 @@ elif os.environ["TLS_FLAVOR"] in [ "mail", "cert" ]:
|
|||||||
|
|
||||||
subprocess.call(["/config.py"])
|
subprocess.call(["/config.py"])
|
||||||
os.system("dovecot -c /etc/dovecot/proxy.conf")
|
os.system("dovecot -c /etc/dovecot/proxy.conf")
|
||||||
os.execv("/usr/sbin/nginx", ["nginx", "-g", "daemon off;"])
|
cmd = ['/usr/sbin/nginx', '-g', 'daemon off;']
|
||||||
|
system.run_process_and_forward_output(cmd)
|
||||||
|
@ -13,8 +13,8 @@ from socrate import system, conf
|
|||||||
system.set_env(log_filters=[
|
system.set_env(log_filters=[
|
||||||
r'(dis)?connect from localhost\[(\:\:1|127\.0\.0\.1)\]( quit=1 commands=1)?$',
|
r'(dis)?connect from localhost\[(\:\:1|127\.0\.0\.1)\]( quit=1 commands=1)?$',
|
||||||
r'haproxy read\: short protocol header\: QUIT$',
|
r'haproxy read\: short protocol header\: QUIT$',
|
||||||
r'discarding EHLO keywords\: PIPELINING$',
|
r'discarding EHLO keywords\: PIPELINING$'
|
||||||
], log_file=os.environ.get('POSTFIX_LOG_FILE'))
|
])
|
||||||
|
|
||||||
os.system("flock -n /queue/pid/master.pid rm /queue/pid/master.pid")
|
os.system("flock -n /queue/pid/master.pid rm /queue/pid/master.pid")
|
||||||
|
|
||||||
@ -100,4 +100,5 @@ os.system("/usr/libexec/postfix/post-install meta_directory=/etc/postfix create-
|
|||||||
# Before starting postfix, we need to check permissions on /queue
|
# Before starting postfix, we need to check permissions on /queue
|
||||||
# in the event that postfix,postdrop id have changed
|
# in the event that postfix,postdrop id have changed
|
||||||
os.system("postfix set-permissions")
|
os.system("postfix set-permissions")
|
||||||
os.system("postfix start-fg")
|
cmd = ['postfix', 'start-fg']
|
||||||
|
system.run_process_and_forward_output(cmd)
|
||||||
|
@ -376,22 +376,6 @@ To disable all plugins just set ``ROUNDCUBE_PLUGINS`` to ``mailu``.
|
|||||||
|
|
||||||
To configure a plugin add php files named ``*.inc.php`` to roundcube's :ref:`override section <override-label>`.
|
To configure a plugin add php files named ``*.inc.php`` to roundcube's :ref:`override section <override-label>`.
|
||||||
|
|
||||||
Mail log settings
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
By default, all services log directly to stdout/stderr. Logs can be collected by any docker log processing solution.
|
|
||||||
|
|
||||||
Postfix writes the logs to a syslog server which logs to stdout. This is used to filter
|
|
||||||
out messages from the healthcheck. In some situations, a separate mail log is required
|
|
||||||
(e.g. for legal reasons). The syslog server can be configured to write log files to a volume.
|
|
||||||
It can be configured with the following option:
|
|
||||||
|
|
||||||
- ``POSTFIX_LOG_FILE``: The file to log the mail log to. When enabled, the syslog server will also log to stdout.
|
|
||||||
|
|
||||||
When ``POSTFIX_LOG_FILE`` is enabled, the logrotate program will automatically rotate the
|
|
||||||
logs every week and keep 52 logs. To override the logrotate configuration, create the file logrotate.conf
|
|
||||||
with the desired configuration in the :ref:`Postfix overrides folder<override-label>`.
|
|
||||||
|
|
||||||
.. _header_authentication:
|
.. _header_authentication:
|
||||||
|
|
||||||
Header authentication using an external proxy
|
Header authentication using an external proxy
|
||||||
|
63
docs/faq.rst
63
docs/faq.rst
@ -451,7 +451,7 @@ down and up again. A container restart is not sufficient.
|
|||||||
SMTP Banner from overrides/postfix.cf is ignored
|
SMTP Banner from overrides/postfix.cf is ignored
|
||||||
````````````````````````````````````````````````
|
````````````````````````````````````````````````
|
||||||
|
|
||||||
Any mail related connection is proxied by nginx. Therefore the SMTP Banner is also set by nginx. Overwriting in overrides/postfix.cf does not apply.
|
Any mail related connection is proxied by the front container. Therefore the SMTP Banner is also set by front container. Overwriting in overrides/postfix.cf does not apply.
|
||||||
|
|
||||||
*Issue reference:* `1368`_.
|
*Issue reference:* `1368`_.
|
||||||
|
|
||||||
@ -922,3 +922,64 @@ I see a lot of "Unable to lookup the TLSA record for XXX. Is the DNSSEC zone oka
|
|||||||
There may be multiple causes for it but if you are running docker 24.0.0, odds are you are `experiencing this docker bug`_ and the workaround is to switch to a different version of docker.
|
There may be multiple causes for it but if you are running docker 24.0.0, odds are you are `experiencing this docker bug`_ and the workaround is to switch to a different version of docker.
|
||||||
|
|
||||||
.. _`experiencing this docker bug`: https://github.com/Mailu/Mailu/issues/2827
|
.. _`experiencing this docker bug`: https://github.com/Mailu/Mailu/issues/2827
|
||||||
|
|
||||||
|
How can I view and export the logs of a Mailu container?
|
||||||
|
````````````````````````````````````````````````````````
|
||||||
|
|
||||||
|
In some situations, a separate log is required. For example a separate mail log (from postfix) could be required due to legal reasons.
|
||||||
|
|
||||||
|
All Mailu containers log the output to journald. The logs are written to journald with the tag:
|
||||||
|
|
||||||
|
| mailu-<service name>
|
||||||
|
| where <service-name> is the name of the service in the docker-compose.yml file.
|
||||||
|
| For example, the service running postfix is called smtp. To view the postfix logs use:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
journalctl -t mailu-smtp
|
||||||
|
|
||||||
|
Note: ``SHIFT+G`` can be used to jump to the end of the log file. ``G`` can be used to jump back to the top of the log file.
|
||||||
|
|
||||||
|
To export the log files from journald to the file system, the logs could be imported into a syslog program like ``rsyslog``.
|
||||||
|
Via ``rsyslog`` the container specific logs could be written to a separate file using a filter.
|
||||||
|
|
||||||
|
Below are the steps for writing the postfix (mail) logs to a log file on the file system.
|
||||||
|
|
||||||
|
1. Install the ``rsyslog`` package. Note: on most distributions this program is already installed.
|
||||||
|
2. Edit ``/etc/systemd/journald.conf``.
|
||||||
|
3. Enable ``ForwardToSyslog=yes``. Note: on most distributions this is already enabled by default. This forwards journald to syslog.
|
||||||
|
4. ``sudo touch /var/log/postfix.log``. This step creates the mail log file.
|
||||||
|
5. ``sudo chown syslog:syslog /var/log/postfix.log``. This provides rsyslog the permissions for accessing this file.
|
||||||
|
6. Create a new config file in ``/etc/rsyslog.d/export-postfix.conf``
|
||||||
|
7. Add ``:programname, contains, "mailu-smtp" /var/log/postfix.log``. This instructs rsyslog to write the logs for mailu-smtp to a log file on file system.
|
||||||
|
8. ``sudo systemctl restart systemd-journald.service``
|
||||||
|
9. ``sudo systemctl restart rsyslog``
|
||||||
|
10. All messages from the smtp/postfix container are now logged to ``/var/log/postfix.log``.
|
||||||
|
11. Rsyslog does not perform log rotation. The program (package) ``log rotate`` can be used for this task. Install the ``logrotate`` package.
|
||||||
|
12. Modify the existing configuration file for rsyslog: ``sudo nano /etc/logrotate.d/rsyslog``
|
||||||
|
13. Add at the top add: ``/var/log/postfix.log``. Of course you can also use your own configuration. This is just an example. A complete example for configuring log rotate is:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
/var/log/postfix.log
|
||||||
|
{
|
||||||
|
rotate 4
|
||||||
|
weekly
|
||||||
|
missingok
|
||||||
|
notifempty
|
||||||
|
compress
|
||||||
|
delaycompress
|
||||||
|
sharedscripts
|
||||||
|
postrotate
|
||||||
|
/usr/lib/rsyslog/rsyslog-rotate
|
||||||
|
endscript
|
||||||
|
}
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
#!/bin/sh
|
||||||
|
#/usr/lib/rsyslog/rsyslog-rotate
|
||||||
|
|
||||||
|
if [ -d /run/systemd/system ]; then
|
||||||
|
systemctl kill -s HUP rsyslog.service
|
||||||
|
fi
|
||||||
|
@ -58,6 +58,10 @@ services:
|
|||||||
resolver:
|
resolver:
|
||||||
image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}unbound:${MAILU_VERSION:-{{ version }}}
|
image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}unbound:${MAILU_VERSION:-{{ version }}}
|
||||||
env_file: {{ env }}
|
env_file: {{ env }}
|
||||||
|
logging:
|
||||||
|
driver: journald
|
||||||
|
options:
|
||||||
|
tag: mailu-resolver
|
||||||
restart: always
|
restart: always
|
||||||
networks:
|
networks:
|
||||||
default:
|
default:
|
||||||
@ -220,7 +224,7 @@ services:
|
|||||||
logging:
|
logging:
|
||||||
driver: journald
|
driver: journald
|
||||||
options:
|
options:
|
||||||
tag: mailu-clamav
|
tag: mailu-antivirus
|
||||||
networks:
|
networks:
|
||||||
- clamav
|
- clamav
|
||||||
volumes:
|
volumes:
|
||||||
@ -237,6 +241,10 @@ services:
|
|||||||
webdav:
|
webdav:
|
||||||
image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}radicale:${MAILU_VERSION:-{{ version }}}
|
image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}radicale:${MAILU_VERSION:-{{ version }}}
|
||||||
restart: always
|
restart: always
|
||||||
|
logging:
|
||||||
|
driver: journald
|
||||||
|
options:
|
||||||
|
tag: mailu-webdav
|
||||||
volumes:
|
volumes:
|
||||||
- "{{ root }}/dav:/data"
|
- "{{ root }}/dav:/data"
|
||||||
networks:
|
networks:
|
||||||
@ -248,6 +256,10 @@ services:
|
|||||||
image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}fetchmail:${MAILU_VERSION:-{{ version }}}
|
image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}fetchmail:${MAILU_VERSION:-{{ version }}}
|
||||||
restart: always
|
restart: always
|
||||||
env_file: {{ env }}
|
env_file: {{ env }}
|
||||||
|
logging:
|
||||||
|
driver: journald
|
||||||
|
options:
|
||||||
|
tag: mailu-fetchmail
|
||||||
volumes:
|
volumes:
|
||||||
- "{{ root }}/data/fetchmail:/data"
|
- "{{ root }}/data/fetchmail:/data"
|
||||||
depends_on:
|
depends_on:
|
||||||
@ -267,6 +279,10 @@ services:
|
|||||||
image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}webmail:${MAILU_VERSION:-{{ version }}}
|
image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}webmail:${MAILU_VERSION:-{{ version }}}
|
||||||
restart: always
|
restart: always
|
||||||
env_file: {{ env }}
|
env_file: {{ env }}
|
||||||
|
logging:
|
||||||
|
driver: journald
|
||||||
|
options:
|
||||||
|
tag: mailu-webmail
|
||||||
volumes:
|
volumes:
|
||||||
- "{{ root }}/webmail:/data"
|
- "{{ root }}/webmail:/data"
|
||||||
- "{{ root }}/overrides/{{ webmail_type }}:/overrides:ro"
|
- "{{ root }}/overrides/{{ webmail_type }}:/overrides:ro"
|
||||||
|
4
towncrier/newsfragments/2939.bugfix
Normal file
4
towncrier/newsfragments/2939.bugfix
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
Fixed log filter not filtering out log messages for dovecot/nginx/postfix.
|
||||||
|
Fixed postfix not logging to standard out.
|
||||||
|
Fixed not all containers logging to journald.
|
||||||
|
Removed POSTFIX_LOG_FILE functionality. Added documentation on how to achieve the same (log to file) via journald & rsyslogd (see new FAQ entry 'How can I view and export the logs of a Mailu container?').
|
Loading…
x
Reference in New Issue
Block a user