瀏覽代碼

[Olefy] A new container is born, thanks to @c-rosenberg
[ACME] Autoconfig is back (re-added to SAN list by default for all mail domains)
[Rspamd] Added comment to composite

andryyy 6 年之前
父節點
當前提交
2efd27e40e

+ 1 - 1
data/Dockerfiles/acme/docker-entrypoint.sh

@@ -244,7 +244,7 @@ while true; do
       ADDITIONAL_SAN_ARR+=($i)
     fi
   done
-  ADDITIONAL_WC_ARR+=('autodiscover')
+  ADDITIONAL_WC_ARR+=('autodiscover' 'autoconfig')
 
   # Start IP detection
   log_f "Detecting IP addresses... " no_nl

+ 6 - 1
data/Dockerfiles/olefy/Dockerfile

@@ -3,12 +3,17 @@ LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
 
 WORKDIR /app
 
+#RUN addgroup -S olefy && adduser -S olefy -G olefy \
 RUN apk add --virtual .build-deps gcc python3-dev musl-dev libffi-dev openssl-dev \
   && apk add --update --no-cache python3 openssl tzdata libmagic \
   && pip3 install --upgrade pip \
   && pip3 install --upgrade oletools asyncio python-magic \
   && apk del .build-deps
 
-COPY olefy.py /app/
+ADD https://raw.githubusercontent.com/HeinleinSupport/olefy/master/olefy.py /app/
+
+RUN chown -R nobody:nobody /app /tmp
+
+USER nobody
 
 CMD ["python3", "-u", "/app/olefy.py"]

+ 0 - 184
data/Dockerfiles/olefy/olefy.py

@@ -1,184 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2019, Dennis Kalbhen <d.kalbhen@heinlein-support.de>
-# Copyright (c) 2019, Carsten Rosenberg <c.rosenberg@heinlein-support.de>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-###
-#
-#  olefy is a little helper socket to use oletools with rspamd. (https://rspamd.com)
-#  Please find updates and issues here: https://github.com/HeinleinSupport/olefy
-#
-###
-
-from subprocess import Popen, PIPE
-import sys
-import os
-import logging
-import asyncio
-import time
-import magic
-
-# merge variables from /etc/olefy.conf and the defaults
-olefy_listen_addr = os.getenv('OLEFY_BINDADDRESS', '127.0.0.1')
-olefy_listen_port = int(os.getenv('OLEFY_BINDPORT', '10050'))
-olefy_tmp_dir = os.getenv('OLEFY_TMPDIR', '/tmp')
-olefy_python_path = os.getenv('OLEFY_PYTHON_PATH', '/usr/bin/python3')
-olefy_olevba_path = os.getenv('OLEFY_OLEVBA_PATH', '/usr/local/bin/olevba3')
-# 10:DEBUG, 20:INFO, 30:WARNING, 40:ERROR, 50:CRITICAL
-olefy_loglvl = int(os.getenv('OLEFY_LOGLVL', 20))
-olefy_min_length = int(os.getenv('OLEFY_MINLENGTH', 500))
-olefy_del_tmp = int(os.getenv('OLEFY_DEL_TMP', 1))
-
-# internal used variables
-request_time = '0000000000.000000'
-olefy_protocol = 'OLEFY'
-olefy_protocol_sep = '\\n\\n'
-olefy_headers = {}
-
-# init logging
-logger = logging.getLogger('olefy')
-logging.basicConfig(stream=sys.stdout, level=olefy_loglvl, format='olefy %(levelname)s %(funcName)s %(message)s')
-
-# log runtime variables
-logger.info('olefy listen address: {}'.format(olefy_listen_addr))
-logger.info('olefy listen port: {}'.format(olefy_listen_port))
-logger.info('olefy tmp dir: {}'.format(olefy_tmp_dir))
-logger.info('olefy python path: {}'.format(olefy_python_path))
-logger.info('olefy olvba path: {}'.format(olefy_olevba_path))
-logger.info('olefy log level: {}'.format(olefy_loglvl))
-logger.info('olefy min file length: {}'.format(olefy_min_length))
-logger.info('olefy delete tmp file: {}'.format(olefy_del_tmp))
-
-if not os.path.isfile(olefy_python_path):
-    logger.critical('python path not found: {}'.format(olefy_python_path))
-    exit(1)
-if not os.path.isfile(olefy_olevba_path):
-    logger.critical('olevba path not found: {}'.format(olefy_olevba_path))
-    exit(1)
-
-# olefy protocol function
-def protocol_split( olefy_line ):
-    header_lines = olefy_line.split('\\n')
-    for line in header_lines:
-        if line == 'OLEFY/1.0':
-            olefy_headers['olefy'] = line
-        elif line != '':
-            kv = line.split(': ')
-            if kv[0] != '' and kv[1] != '':
-                olefy_headers[kv[0]] = kv[1]
-    logger.debug('olefy_headers: {}'.format(olefy_headers))
-
-# calling oletools
-def oletools( stream, tmp_file_name, lid ):
-    if olefy_min_length > stream.__len__():
-        logger.error('{} {} bytes (Not Scanning! File smaller than {!r})'.format(lid, stream.__len__(), olefy_min_length))
-        out = b'[ { "error": "File too small" } ]'
-    else:
-        tmp_file = open(tmp_file_name, 'wb')
-        tmp_file.write(stream)
-        tmp_file.close()
-
-        file_magic = magic.Magic(mime=True, uncompress=True)
-        file_mime = file_magic.from_file(tmp_file_name)
-        logger.info('{} {} (libmagic output)'.format(lid, file_mime))
-
-        # do the olefy
-        cmd_tmp = Popen([olefy_python_path, olefy_olevba_path, '-a', '-j', tmp_file_name], stdout=PIPE, stderr=PIPE)
-        out, err = cmd_tmp.communicate()
-        if out.__len__() < 10:
-            logger.error('{} olevba returned <10 chars - rc: {!r}, response: {!r}'.format(lid,cmd_tmp.returncode, out.decode('ascii')))
-            out = b'[ { "error": "Unhandled oletools response" } ]'
-        if err.__len__() > 10:
-            logger.error('{} olevba stderr >10 chars - rc: {!r}, response: {!r}'.format(lid, cmd_tmp.returncode, err.decode('ascii')))
-            out = b'[ { "error": "Unhandled oletools error" } ]'
-        if cmd_tmp.returncode != 0:
-            logger.error('{} olevba exited with code {!r}; err: {!r}'.format(lid, cmd_tmp.returncode, err.decode('ascii')))
-
-        if olefy_del_tmp == 1:
-            logger.debug('{} {} deleting tmp file'.format(lid, tmp_file_name))
-            os.remove(tmp_file_name)
-
-    logger.debug('{} response: {}'.format(lid, out.decode()))
-    return out
-
-# Asyncio data handling, default AIO-Functions
-class AIO(asyncio.Protocol):
-    def __init__(self):
-        self.extra = bytearray()
-
-    def connection_made(self, transport):
-        global request_time
-        peer = transport.get_extra_info('peername')
-        logger.debug('{} new connection was made'.format(peer))
-        self.transport = transport
-        request_time = str(time.time())
-
-    def data_received(self, request, msgid=1):
-        peer = self.transport.get_extra_info('peername')
-        logger.debug('{} data received from new connection'.format(peer))
-        self.extra.extend(request)
-
-    def eof_received(self):
-        peer = self.transport.get_extra_info('peername')
-        olefy_protocol_err = False
-        proto_ck = str(self.extra[0:2000])
-        if olefy_protocol in proto_ck:
-            olefy_line = proto_ck[12:proto_ck.find(olefy_protocol_sep)]
-            self.extra = bytearray(self.extra[59:len(self.extra)])
-            protocol_split(olefy_line)
-        else:
-            olefy_protocol_err = True
-
-        lid = 'Rspamd-ID' in olefy_headers and '<'+olefy_headers['Rspamd-ID'][:6]+'>' or '<>'
-
-        tmp_file_name = olefy_tmp_dir+'/'+request_time+'.'+str(peer[1])
-        logger.debug('{} {} choosen as tmp filename'.format(lid, tmp_file_name))
-
-        logger.info('{} {} bytes (stream size)'.format(lid, self.extra.__len__()))
-
-        if olefy_protocol_err == True or olefy_headers['olefy'] != 'OLEFY/1.0':
-            logger.error('Protocol ERROR: no OLEFY/1.0 found)')
-            out = b'[ { "error": "Protocol error" } ]'
-        elif 'Method' in olefy_headers:
-            if olefy_headers['Method'] == 'oletools':
-                out = oletools(self.extra, tmp_file_name, lid)
-        else:
-            logger.error('Protocol ERROR: Method header not found')
-            out = b'[ { "error": "Protocol error: Method header not found" } ]'
-
-        self.transport.write(out)
-        logger.info('{} response send: {!r}'.format(peer, out))
-        self.transport.close()
-
-
-# start the listeners
-loop = asyncio.get_event_loop()
-# each client connection will create a new protocol instance
-coro = loop.create_server(AIO, olefy_listen_addr, olefy_listen_port)
-server = loop.run_until_complete(coro)
-logger.info('serving on {}'.format(server.sockets[0].getsockname()))
-
-# XXX serve requests until KeyboardInterrupt, not needed for production
-try:
-    loop.run_forever()
-except KeyboardInterrupt:
-    pass
-
-# graceful shutdown/reload
-server.close()
-loop.run_until_complete(server.wait_closed())
-loop.close()
-logger.info('stopped serving')

+ 1 - 0
data/conf/rspamd/local.d/composites.conf

@@ -20,6 +20,7 @@ SPOOFED_UNAUTH {
   expression = "!MAILCOW_AUTH & !MAILCOW_WHITE & !R_SPF_ALLOW & !DMARC_POLICY_ALLOW & !ARC_ALLOW & !SIEVE_HOST & MAILCOW_DOMAIN_HEADER_FROM";
   score = 5.0;
 }
+# Only apply to inbound unauthed and not whitelisted
 OLEFY_MACRO {
   expression = "!MAILCOW_AUTH & !MAILCOW_WHITE & OLETOOLS";
   score = 20.0;