experimental mail transport

This commit is contained in:
j3d1 2023-11-23 23:17:20 +01:00
parent e43d4837c3
commit 191c8d822b
4 changed files with 115 additions and 17 deletions

View file

@ -1,9 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import asyncio import asyncio
import logging import logging
import os
from abc import ABCMeta
import uvicorn import uvicorn
from aiosmtpd.controller import Controller, UnixSocketController from aiosmtpd.controller import Controller, UnixSocketController, BaseController, UnixSocketMixin
from aiosmtpd.lmtp import LMTP from aiosmtpd.lmtp import LMTP
@ -47,17 +49,39 @@ class LTMPController(Controller):
return LMTP(self.handler) return LMTP(self.handler)
class UnixSocketLMTPController(UnixSocketController): class BaseAsyncController(BaseController, metaclass=ABCMeta):
def __init__(
self,
handler,
loop,
**SMTP_parameters,
):
super().__init__(
handler,
loop,
**SMTP_parameters,
)
def serve(self):
return self._create_server()
class UnixSocketLMTPController(UnixSocketMixin, BaseAsyncController):
def factory(self): def factory(self):
return LMTP(self.handler) return LMTP(self.handler)
def _trigger_server(self): # pragma: no-unixsock
# Prevent confusion on which _trigger_server() to invoke.
# Or so LGTM.com claimed
UnixSocketMixin._trigger_server(self)
class UvicornServer(uvicorn.Server): class UvicornServer(uvicorn.Server):
def install_signal_handlers(self): def install_signal_handlers(self):
pass pass
async def web(): async def web(loop):
log_config = uvicorn.config.LOGGING_CONFIG log_config = uvicorn.config.LOGGING_CONFIG
log_config["handlers"]["default"] = {"class": "logging.FileHandler", "filename": "web.log", "formatter": "default"} log_config["handlers"]["default"] = {"class": "logging.FileHandler", "filename": "web.log", "formatter": "default"}
log_config["handlers"]["access"] = {"class": "logging.FileHandler", "filename": "web-access.log", log_config["handlers"]["access"] = {"class": "logging.FileHandler", "filename": "web-access.log",
@ -67,8 +91,10 @@ async def web():
await server.serve() await server.serve()
async def tcp(): async def tcp(loop):
log = logging.getLogger('test') log = logging.getLogger('test.log')
log.addHandler(logging.FileHandler('test.log'))
log.setLevel(logging.DEBUG)
log.info("Starting TCP server") log.info("Starting TCP server")
server = await asyncio.start_unix_server(handle_echo, path='test.sock') server = await asyncio.start_unix_server(handle_echo, path='test.sock')
@ -80,11 +106,20 @@ async def tcp():
log.info("TCP done") log.info("TCP done")
async def lmtp(): async def lmtp(loop):
log = logging.getLogger('lmtp') log = logging.getLogger('mail.log')
log.addHandler(logging.FileHandler('mail.log'))
log.setLevel(logging.DEBUG)
log.info("Starting LMTP server") log.info("Starting LMTP server")
cont = UnixSocketLMTPController(ExampleHandler(), unix_socket='lmtp.sock') server = await UnixSocketLMTPController(ExampleHandler(), unix_socket='lmtp.sock', loop=loop).serve()
cont.start()
addrs = ', '.join(str(sock.getsockname()) for sock in server.sockets)
log.info(f'Serving on {addrs}')
os.chmod('lmtp.sock', 0o775)
async with server:
await server.serve_forever()
log.info("LMTP done") log.info("LMTP done")
@ -104,18 +139,18 @@ def main():
import sdnotify import sdnotify
import signal import signal
import setproctitle import setproctitle
import os
setproctitle.setproctitle("c3lf-sys3") setproctitle.setproctitle("c3lf-sys3")
logging.basicConfig(filename='server.log', level=logging.DEBUG, encoding='utf-8') log = logging.getLogger('server.log')
logging.basicConfig(filename='test.log', level=logging.DEBUG, encoding='utf-8') log.addHandler(logging.FileHandler('server.log'))
logging.basicConfig(filename='lmtp.log', level=logging.DEBUG, encoding='utf-8') log.setLevel(logging.DEBUG)
log = logging.getLogger()
log.info("Starting server") log.info("Starting server")
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
loop.add_signal_handler(signal.SIGTERM, lambda: asyncio.create_task(shutdown(signal.SIGTERM, loop))) loop.add_signal_handler(signal.SIGTERM, lambda: asyncio.create_task(shutdown(signal.SIGTERM, loop)))
loop.add_signal_handler(signal.SIGINT, lambda: asyncio.create_task(shutdown(signal.SIGINT, loop))) loop.add_signal_handler(signal.SIGINT, lambda: asyncio.create_task(shutdown(signal.SIGINT, loop)))
loop.create_task(web()) loop.create_task(web(loop))
loop.create_task(tcp()) loop.create_task(tcp(loop))
loop.create_task(lmtp()) loop.create_task(lmtp(loop))
n = sdnotify.SystemdNotifier() n = sdnotify.SystemdNotifier()
n.notify("READY=1") n.notify("READY=1")
log.info("Server ready") log.info("Server ready")
@ -123,8 +158,13 @@ def main():
loop.run_forever() loop.run_forever()
finally: finally:
loop.close() loop.close()
os.remove("lmtp.sock")
os.remove("test.sock")
os.remove("web.sock")
logging.info("Server stopped") logging.info("Server stopped")
logging.shutdown()
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View file

@ -8,6 +8,7 @@ c3lf-nodes:
git_branch: master git_branch: master
git_repo: <git_repo_url> git_repo: <git_repo_url>
db_password: <db_password> db_password: <db_password>
mail_domain: <mail_domain>
main_email: <main_email> main_email: <main_email>
legacy_api_user: <legacy_api_user> legacy_api_user: <legacy_api_user>
legacy_api_password: <legacy_api_password> legacy_api_password: <legacy_api_password>

View file

@ -274,4 +274,11 @@
service: service:
name: c3lf-sys3 name: c3lf-sys3
state: started state: started
enabled: yes enabled: yes
- name: configure postfix
template:
src: templates/postfix.cf.j2
dest: /etc/postfix/main.cf
notify:
- restart postfix

View file

@ -0,0 +1,50 @@
# See /usr/share/postfix/main.cf.dist for a commented, more complete version
# Debian specific: Specifying a file name will cause the first
# line of that file to be used as the name. The Debian default
# is /etc/mailname.
#myorigin = /etc/mailname
smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
biff = no
# appending .domain is the MUA's job.
append_dot_mydomain = no
readme_directory = no
# See http://www.postfix.org/COMPATIBILITY_README.html -- default to 3.6 on
# fresh installs.
compatibility_level = 3.6
# TLS parameters
smtp_use_tls = yes
smtp_force_tls = yes
smtpd_use_tls = yes
smtpd_tls_cert_file=/etc/letsencrypt/live/{{ web_domain }}/fullchain.pem;
smtpd_tls_key_file=/etc/letsencrypt/live/{{ web_domain }}/privkey.pem;
smtpd_tls_security_level=may
smtp_tls_CApath=/etc/ssl/certs
smtp_tls_security_level=may
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
myhostname = polaris.c3lf.de
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = /etc/mailname
mydestination = $myhostname, polaris.c3lf.de, localhost.c3lf.de, , localhost, {{ mail_domain }}
#relayhost = firefly.lab.or.it
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
inet_protocols = all
maillog_file = /var/log/mail.log
virtual_transport=lmtp:unix:/var/www/c3lf-sys3/lmtp.sock