From 3136e020f6c0afed83feaddddbd05fff2029fc92 Mon Sep 17 00:00:00 2001 From: andryyy Date: Fri, 9 Aug 2019 14:08:58 +0200 Subject: [PATCH] [Postfix] Remove Zeyple, base on Buster --- data/Dockerfiles/postfix/Dockerfile | 19 +- data/Dockerfiles/postfix/postfix.sh | 11 +- data/Dockerfiles/postfix/syslog-ng.conf | 3 +- data/Dockerfiles/postfix/zeyple.conf | 9 - data/Dockerfiles/postfix/zeyple.py | 274 ------------------------ 5 files changed, 15 insertions(+), 301 deletions(-) delete mode 100644 data/Dockerfiles/postfix/zeyple.conf delete mode 100755 data/Dockerfiles/postfix/zeyple.py diff --git a/data/Dockerfiles/postfix/Dockerfile b/data/Dockerfiles/postfix/Dockerfile index 05f2c3c7..b3567cfa 100644 --- a/data/Dockerfiles/postfix/Dockerfile +++ b/data/Dockerfiles/postfix/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:bionic +FROM debian:buster-slim LABEL maintainer "Andre Peters " ARG DEBIAN_FRONTEND=noninteractive @@ -9,12 +9,17 @@ RUN dpkg-divert --local --rename --add /sbin/initctl \ && dpkg-divert --local --rename --add /usr/bin/ischroot \ && ln -sf /bin/true /usr/bin/ischroot -RUN apt-get update && apt-get install -y --no-install-recommends \ +# Add groups and users before installing Postfix to not break compatibility +RUN groupadd -g 102 postfix \ + && groupadd -g 103 postdrop \ + && useradd -g postfix -u 101 -d /var/spool/postfix -s /usr/sbin/nologin postfix \ + && apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ curl \ dirmngr \ gnupg \ libsasl2-modules \ + mariadb-client \ perl \ postfix \ postfix-mysql \ @@ -32,16 +37,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && printf '#!/bin/bash\n/usr/sbin/postconf -c /opt/postfix/conf "$@"' > /usr/local/sbin/postconf \ && chmod +x /usr/local/sbin/postconf -RUN addgroup --system --gid 600 zeyple \ - && adduser --system --home /var/lib/zeyple --no-create-home --uid 600 --gid 600 --disabled-login zeyple \ - && touch /var/log/zeyple.log \ - && chown zeyple: /var/log/zeyple.log \ - && mkdir -p /opt/mailman/var/data \ - && touch /opt/mailman/var/data/postfix_lmtp \ - && touch /opt/mailman/var/data/postfix_domains - -COPY zeyple.py /usr/local/bin/zeyple.py -COPY zeyple.conf /etc/zeyple.conf COPY supervisord.conf /etc/supervisor/supervisord.conf COPY syslog-ng.conf /etc/syslog-ng/syslog-ng.conf COPY postfix.sh /opt/postfix.sh diff --git a/data/Dockerfiles/postfix/postfix.sh b/data/Dockerfiles/postfix/postfix.sh index 90afb89e..67b9c247 100755 --- a/data/Dockerfiles/postfix/postfix.sh +++ b/data/Dockerfiles/postfix/postfix.sh @@ -4,6 +4,12 @@ trap "postfix stop" EXIT [[ ! -d /opt/postfix/conf/sql/ ]] && mkdir -p /opt/postfix/conf/sql/ +# Wait for MySQL to warm-up +while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do + echo "Waiting for database to come up..." + sleep 2 +done + cat < /etc/aliases # Autogenerated by mailcow null: /dev/null @@ -286,11 +292,6 @@ query = SELECT goto FROM spamalias AND validity >= UNIX_TIMESTAMP() EOF -# Reset GPG key permissions -mkdir -p /var/lib/zeyple/keys -chmod 700 /var/lib/zeyple/keys -chown -R 600:600 /var/lib/zeyple/keys - # Fix Postfix permissions chown -R root:postfix /opt/postfix/conf/sql/ chmod 640 /opt/postfix/conf/sql/*.cf diff --git a/data/Dockerfiles/postfix/syslog-ng.conf b/data/Dockerfiles/postfix/syslog-ng.conf index 5d11a3b7..21044719 100644 --- a/data/Dockerfiles/postfix/syslog-ng.conf +++ b/data/Dockerfiles/postfix/syslog-ng.conf @@ -1,9 +1,10 @@ -@version: 3.13 +@version: 3.19 @include "scl.conf" options { chain_hostnames(off); flush_lines(0); use_dns(no); + dns_cache(no); use_fqdn(no); owner("root"); group("adm"); perm(0640); stats_freq(0); diff --git a/data/Dockerfiles/postfix/zeyple.conf b/data/Dockerfiles/postfix/zeyple.conf deleted file mode 100644 index cc176a0e..00000000 --- a/data/Dockerfiles/postfix/zeyple.conf +++ /dev/null @@ -1,9 +0,0 @@ -[zeyple] -log_file = /dev/null - -[gpg] -home = /var/lib/zeyple/keys - -[relay] -host = localhost -port = 10026 diff --git a/data/Dockerfiles/postfix/zeyple.py b/data/Dockerfiles/postfix/zeyple.py deleted file mode 100755 index bb218831..00000000 --- a/data/Dockerfiles/postfix/zeyple.py +++ /dev/null @@ -1,274 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import sys -import os -import logging -import email -import email.mime.multipart -import email.mime.application -import email.encoders -import smtplib -import copy -from io import BytesIO - -try: - from configparser import SafeConfigParser # Python 3 -except ImportError: - from ConfigParser import SafeConfigParser # Python 2 - -import gpgme - -# Boiler plate to avoid dependency on six -# BBB: Python 2.7 support -PY3K = sys.version_info > (3, 0) - - -def message_from_binary(message): - if PY3K: - return email.message_from_bytes(message) - else: - return email.message_from_string(message) - - -def as_binary_string(email): - if PY3K: - return email.as_bytes() - else: - return email.as_string() - - -def encode_string(string): - if isinstance(string, bytes): - return string - else: - return string.encode('utf-8') - - -__title__ = 'Zeyple' -__version__ = '1.2.0' -__author__ = 'Cédric Félizard' -__license__ = 'AGPLv3+' -__copyright__ = 'Copyright 2012-2016 Cédric Félizard' - - -class Zeyple: - """Zeyple Encrypts Your Precious Log Emails""" - - def __init__(self, config_fname='zeyple.conf'): - self.config = self.load_configuration(config_fname) - - log_file = self.config.get('zeyple', 'log_file') - logging.basicConfig( - filename=log_file, level=logging.DEBUG, - format='%(asctime)s %(process)s %(levelname)s %(message)s' - ) - logging.info("Zeyple ready to encrypt outgoing emails") - - def load_configuration(self, filename): - """Reads and parses the config file""" - - config = SafeConfigParser() - config.read([ - os.path.join('/etc/', filename), - filename, - ]) - if not config.sections(): - raise IOError('Cannot open config file.') - return config - - @property - def gpg(self): - protocol = gpgme.PROTOCOL_OpenPGP - - if self.config.has_option('gpg', 'executable'): - executable = self.config.get('gpg', 'executable') - else: - executable = None # Default value - - home_dir = self.config.get('gpg', 'home') - - ctx = gpgme.Context() - ctx.set_engine_info(protocol, executable, home_dir) - ctx.armor = True - - return ctx - - def process_message(self, message_data, recipients): - """Encrypts the message with recipient keys""" - message_data = encode_string(message_data) - - in_message = message_from_binary(message_data) - logging.info( - "Processing outgoing message %s", in_message['Message-id']) - - if not recipients: - logging.warn("Cannot find any recipients, ignoring") - - sent_messages = [] - for recipient in recipients: - logging.info("Recipient: %s", recipient) - - key_id = self._user_key(recipient) - logging.info("Key ID: %s", key_id) - - if key_id: - out_message = self._encrypt_message(in_message, key_id) - - # Delete Content-Transfer-Encoding if present to default to - # "7bit" otherwise Thunderbird seems to hang in some cases. - del out_message["Content-Transfer-Encoding"] - else: - logging.warn("No keys found, message will be sent unencrypted") - out_message = copy.copy(in_message) - - self._add_zeyple_header(out_message) - self._send_message(out_message, recipient) - sent_messages.append(out_message) - - return sent_messages - - def _get_version_part(self): - ret = email.mime.application.MIMEApplication( - 'Version: 1\n', - 'pgp-encrypted', - email.encoders.encode_noop, - ) - ret.add_header( - 'Content-Description', - "PGP/MIME version identification", - ) - return ret - - def _get_encrypted_part(self, payload): - ret = email.mime.application.MIMEApplication( - payload, - 'octet-stream', - email.encoders.encode_noop, - name="encrypted.asc", - ) - ret.add_header('Content-Description', "OpenPGP encrypted message") - ret.add_header( - 'Content-Disposition', - 'inline', - filename='encrypted.asc', - ) - return ret - - def _encrypt_message(self, in_message, key_id): - if in_message.is_multipart(): - # get the body (after the first \n\n) - payload = in_message.as_string().split("\n\n", 1)[1].strip() - - # prepend the Content-Type including the boundary - content_type = "Content-Type: " + in_message["Content-Type"] - payload = content_type + "\n\n" + payload - - message = email.message.Message() - message.set_payload(payload) - - payload = message.get_payload() - - else: - payload = in_message.get_payload() - payload = encode_string(payload) - - quoted_printable = email.charset.Charset('ascii') - quoted_printable.body_encoding = email.charset.QP - - message = email.mime.nonmultipart.MIMENonMultipart( - 'text', 'plain', charset='utf-8' - ) - message.set_payload(payload, charset=quoted_printable) - - mixed = email.mime.multipart.MIMEMultipart( - 'mixed', - None, - [message], - ) - - # remove superfluous header - del mixed['MIME-Version'] - - payload = as_binary_string(mixed) - - encrypted_payload = self._encrypt_payload(payload, [key_id]) - - version = self._get_version_part() - encrypted = self._get_encrypted_part(encrypted_payload) - - out_message = copy.copy(in_message) - out_message.preamble = "This is an OpenPGP/MIME encrypted " \ - "message (RFC 4880 and 3156)" - - if 'Content-Type' not in out_message: - out_message['Content-Type'] = 'multipart/encrypted' - else: - out_message.replace_header( - 'Content-Type', - 'multipart/encrypted', - ) - - out_message.set_param('protocol', 'application/pgp-encrypted') - out_message.set_payload([version, encrypted]) - - return out_message - - def _encrypt_payload(self, payload, key_ids): - """Encrypts the payload with the given keys""" - payload = encode_string(payload) - - plaintext = BytesIO(payload) - ciphertext = BytesIO() - - self.gpg.armor = True - - recipient = [self.gpg.get_key(key_id) for key_id in key_ids] - - self.gpg.encrypt(recipient, gpgme.ENCRYPT_ALWAYS_TRUST, - plaintext, ciphertext) - - return ciphertext.getvalue() - - def _user_key(self, email): - """Returns the GPG key for the given email address""" - logging.info("Trying to encrypt for %s", email) - keys = [key for key in self.gpg.keylist(email)] - - if keys: - key = keys.pop() # NOTE: looks like keys[0] is the master key - key_id = key.subkeys[0].keyid - return key_id - - return None - - def _add_zeyple_header(self, message): - if self.config.has_option('zeyple', 'add_header') and \ - self.config.getboolean('zeyple', 'add_header'): - message.add_header( - 'X-Zeyple', - "processed by {0} v{1}".format(__title__, __version__) - ) - - def _send_message(self, message, recipient): - """Sends the given message through the SMTP relay""" - logging.info("Sending message %s", message['Message-id']) - - smtp = smtplib.SMTP(self.config.get('relay', 'host'), - self.config.get('relay', 'port')) - - smtp.sendmail(message['From'], recipient, message.as_string()) - smtp.quit() - - logging.info("Message %s sent", message['Message-id']) - - -if __name__ == '__main__': - recipients = sys.argv[1:] - - # BBB: Python 2.7 support - binary_stdin = sys.stdin.buffer if PY3K else sys.stdin - message = binary_stdin.read() - - zeyple = Zeyple() - zeyple.process_message(message, recipients)