4 files + 57 − 5 Side-by-side Compare changes Side-by-side Inline Show whitespace changes Files 4 .gitlab-ci.yml +6 −2 Original line number Original line Diff line number Diff line Loading @@ -74,7 +74,9 @@ unittests:buster: - htmlcov - htmlcov expose_as: 'Coverage Report' expose_as: 'Coverage Report' reports: reports: #cobertura: coverage.xml #coverage_report: # coverage_format: cobertura # path: coverage.xml junit: report.xml junit: report.xml #coverage: '/^TOTAL.*\s+(\d+\%)$/' #coverage: '/^TOTAL.*\s+(\d+\%)$/' Loading @@ -93,7 +95,9 @@ unittests:bullseye: - htmlcov - htmlcov expose_as: 'Coverage Report' expose_as: 'Coverage Report' reports: reports: cobertura: coverage.xml coverage_report: coverage_format: cobertura path: coverage.xml junit: report.xml junit: report.xml coverage: '/^TOTAL.*\s+(\d+\%)$/' coverage: '/^TOTAL.*\s+(\d+\%)$/' Loading README.md +9 −1 Original line number Original line Diff line number Diff line Loading @@ -7,7 +7,7 @@ for a given address. uffd-socketmapd uses this API to integrate alias lookup with MTAs that support the socketmap protocol, like sendmail and postfix. with MTAs that support the socketmap protocol, like sendmail and postfix. uffd-socketmapd can be run manually. For production deployments, use the uffd-socketmapd can be run manually. For production deployments, use the provided debian packages. Add our package mirror to `/etc/sources.list`: provided Debian packages. Add our package mirror to `/etc/sources.list`: ``` ``` deb https://packages.cccv.de/uffd bullseye main deb https://packages.cccv.de/uffd bullseye main Loading @@ -26,6 +26,14 @@ by adding the following lines to `/etc/postfix/main.cf`: virtual_alias_maps = socketmap:unix:/uffd-socketmapd.sock:virtual virtual_alias_maps = socketmap:unix:/uffd-socketmapd.sock:virtual # Defaults to $virtual_alias_maps, which does not work here, so unset it # Defaults to $virtual_alias_maps, which does not work here, so unset it virtual_alias_domains = virtual_alias_domains = # Optional: To use uffd's remailer feature, setup address rewriting. Remailer # recipient addresses will be rewritten in both the envelope (like virtual # aliases) and the message headers. Make sure that rewriting takes place # before DKIM signing and that it is only applied to messages from your # services (see local_header_rewrite_clients). recipient_canonical_maps = socketmap:unix:/uffd-socketmapd.sock:remailer_canonical local_header_rewrite_clients = permit_inet_interfaces permit_sasl_authenticated ``` ``` Note that uffd-socketmapd requires at least uffd v1.2.0! Note that uffd-socketmapd requires at least uffd v1.2.0! test_server.py +31 −2 Original line number Original line Diff line number Diff line Loading @@ -60,14 +60,18 @@ class MockConnection: pass pass class UffdAPIMock: class UffdAPIMock: def __init__(self, aliases=None): def __init__(self, aliases=None, remailer_map=None): self.aliases = aliases or {} self.aliases = aliases or {} self.remailer_map = remailer_map or {} def get_aliases(self, mail_address): def get_aliases(self, mail_address): return self.aliases.get(mail_address, []) return self.aliases.get(mail_address, []) def resolve_remailer(self, orig_address): return self.remailer_map.get(orig_address, None) class TestSocketmapsRequestHandler(unittest.TestCase): class TestSocketmapsRequestHandler(unittest.TestCase): def test_handle(self): def test_handle_virtual(self): api = UffdAPIMock(aliases={ api = UffdAPIMock(aliases={ 'test@example.com': ['test-dest@example.com'], 'test@example.com': ['test-dest@example.com'], 'space test@example.com': ['space test dest@example.com'], 'space test@example.com': ['space test dest@example.com'], Loading Loading @@ -136,3 +140,28 @@ class TestSocketmapsRequestHandler(unittest.TestCase): conn = MockConnection(request, 4096) conn = MockConnection(request, 4096) RequestHandler(conn, '', None).handle() RequestHandler(conn, '', None).handle() self.assertIn(b'PERM ', conn.sent) self.assertIn(b'PERM ', conn.sent) def test_handle_remailer(self): api = UffdAPIMock(remailer_map={ 'v1-23-testuser@remailer.example.com': 'testuser@example.com', 'v1-23-testadmin@remailer.example.com': 'testadmin@example.com', }) RequestHandler = make_requesthandler(api) request = b'54:remailer_canonical v1-23-testuser@remailer.example.com,' response = b'23:OK testuser@example.com,' conn = MockConnection(request, 4096) RequestHandler(conn, '', None).handle() self.assertEqual(conn.sent, response) request = b'56:remailer_canonical v1-42-not-a-user@remailer.example.com,' response = b'9:NOTFOUND ,' conn = MockConnection(request, 4096) RequestHandler(conn, '', None).handle() self.assertEqual(conn.sent, response) request = b'35:remailer_canonical test@example.com,' response = b'9:NOTFOUND ,' conn = MockConnection(request, 4096) RequestHandler(conn, '', None).handle() self.assertEqual(conn.sent, response) uffd-socketmapd +11 −0 Original line number Original line Diff line number Diff line Loading @@ -30,6 +30,11 @@ class UffdAPI: logger.debug('API getmails response for %s: %s', repr(mail_address), destinations) logger.debug('API getmails response for %s: %s', repr(mail_address), destinations) return destinations return destinations def resolve_remailer(self, orig_address): '''Return real mail address for a given remailer mail address or None''' result = self.get('/api/v1/resolve-remailer', orig_address=orig_address) return result['address'] # From https://cr.yp.to/proto/netstrings.txt # From https://cr.yp.to/proto/netstrings.txt # # # Any string of 8-bit bytes may be encoded as [len]":"[string]",". # Any string of 8-bit bytes may be encoded as [len]":"[string]",". Loading Loading @@ -188,6 +193,12 @@ class SocketmapsRequestHandler(socketserver.BaseRequestHandler): # matter, since Postfix internally processes lookup results as a # matter, since Postfix internally processes lookup results as a # comma-separated list. # comma-separated list. return 'OK ' + ','.join(results) return 'OK ' + ','.join(results) if name == 'remailer_canonical': domain = key.rsplit('@', 1)[-1] result = self.api.resolve_remailer(key) if result is None: return 'NOTFOUND ' return 'OK ' + result return 'PERM Unknown request name', logging.WARNING return 'PERM Unknown request name', logging.WARNING def make_requesthandler(api): def make_requesthandler(api): Loading
.gitlab-ci.yml +6 −2 Original line number Original line Diff line number Diff line Loading @@ -74,7 +74,9 @@ unittests:buster: - htmlcov - htmlcov expose_as: 'Coverage Report' expose_as: 'Coverage Report' reports: reports: #cobertura: coverage.xml #coverage_report: # coverage_format: cobertura # path: coverage.xml junit: report.xml junit: report.xml #coverage: '/^TOTAL.*\s+(\d+\%)$/' #coverage: '/^TOTAL.*\s+(\d+\%)$/' Loading @@ -93,7 +95,9 @@ unittests:bullseye: - htmlcov - htmlcov expose_as: 'Coverage Report' expose_as: 'Coverage Report' reports: reports: cobertura: coverage.xml coverage_report: coverage_format: cobertura path: coverage.xml junit: report.xml junit: report.xml coverage: '/^TOTAL.*\s+(\d+\%)$/' coverage: '/^TOTAL.*\s+(\d+\%)$/' Loading
README.md +9 −1 Original line number Original line Diff line number Diff line Loading @@ -7,7 +7,7 @@ for a given address. uffd-socketmapd uses this API to integrate alias lookup with MTAs that support the socketmap protocol, like sendmail and postfix. with MTAs that support the socketmap protocol, like sendmail and postfix. uffd-socketmapd can be run manually. For production deployments, use the uffd-socketmapd can be run manually. For production deployments, use the provided debian packages. Add our package mirror to `/etc/sources.list`: provided Debian packages. Add our package mirror to `/etc/sources.list`: ``` ``` deb https://packages.cccv.de/uffd bullseye main deb https://packages.cccv.de/uffd bullseye main Loading @@ -26,6 +26,14 @@ by adding the following lines to `/etc/postfix/main.cf`: virtual_alias_maps = socketmap:unix:/uffd-socketmapd.sock:virtual virtual_alias_maps = socketmap:unix:/uffd-socketmapd.sock:virtual # Defaults to $virtual_alias_maps, which does not work here, so unset it # Defaults to $virtual_alias_maps, which does not work here, so unset it virtual_alias_domains = virtual_alias_domains = # Optional: To use uffd's remailer feature, setup address rewriting. Remailer # recipient addresses will be rewritten in both the envelope (like virtual # aliases) and the message headers. Make sure that rewriting takes place # before DKIM signing and that it is only applied to messages from your # services (see local_header_rewrite_clients). recipient_canonical_maps = socketmap:unix:/uffd-socketmapd.sock:remailer_canonical local_header_rewrite_clients = permit_inet_interfaces permit_sasl_authenticated ``` ``` Note that uffd-socketmapd requires at least uffd v1.2.0! Note that uffd-socketmapd requires at least uffd v1.2.0!
test_server.py +31 −2 Original line number Original line Diff line number Diff line Loading @@ -60,14 +60,18 @@ class MockConnection: pass pass class UffdAPIMock: class UffdAPIMock: def __init__(self, aliases=None): def __init__(self, aliases=None, remailer_map=None): self.aliases = aliases or {} self.aliases = aliases or {} self.remailer_map = remailer_map or {} def get_aliases(self, mail_address): def get_aliases(self, mail_address): return self.aliases.get(mail_address, []) return self.aliases.get(mail_address, []) def resolve_remailer(self, orig_address): return self.remailer_map.get(orig_address, None) class TestSocketmapsRequestHandler(unittest.TestCase): class TestSocketmapsRequestHandler(unittest.TestCase): def test_handle(self): def test_handle_virtual(self): api = UffdAPIMock(aliases={ api = UffdAPIMock(aliases={ 'test@example.com': ['test-dest@example.com'], 'test@example.com': ['test-dest@example.com'], 'space test@example.com': ['space test dest@example.com'], 'space test@example.com': ['space test dest@example.com'], Loading Loading @@ -136,3 +140,28 @@ class TestSocketmapsRequestHandler(unittest.TestCase): conn = MockConnection(request, 4096) conn = MockConnection(request, 4096) RequestHandler(conn, '', None).handle() RequestHandler(conn, '', None).handle() self.assertIn(b'PERM ', conn.sent) self.assertIn(b'PERM ', conn.sent) def test_handle_remailer(self): api = UffdAPIMock(remailer_map={ 'v1-23-testuser@remailer.example.com': 'testuser@example.com', 'v1-23-testadmin@remailer.example.com': 'testadmin@example.com', }) RequestHandler = make_requesthandler(api) request = b'54:remailer_canonical v1-23-testuser@remailer.example.com,' response = b'23:OK testuser@example.com,' conn = MockConnection(request, 4096) RequestHandler(conn, '', None).handle() self.assertEqual(conn.sent, response) request = b'56:remailer_canonical v1-42-not-a-user@remailer.example.com,' response = b'9:NOTFOUND ,' conn = MockConnection(request, 4096) RequestHandler(conn, '', None).handle() self.assertEqual(conn.sent, response) request = b'35:remailer_canonical test@example.com,' response = b'9:NOTFOUND ,' conn = MockConnection(request, 4096) RequestHandler(conn, '', None).handle() self.assertEqual(conn.sent, response)
uffd-socketmapd +11 −0 Original line number Original line Diff line number Diff line Loading @@ -30,6 +30,11 @@ class UffdAPI: logger.debug('API getmails response for %s: %s', repr(mail_address), destinations) logger.debug('API getmails response for %s: %s', repr(mail_address), destinations) return destinations return destinations def resolve_remailer(self, orig_address): '''Return real mail address for a given remailer mail address or None''' result = self.get('/api/v1/resolve-remailer', orig_address=orig_address) return result['address'] # From https://cr.yp.to/proto/netstrings.txt # From https://cr.yp.to/proto/netstrings.txt # # # Any string of 8-bit bytes may be encoded as [len]":"[string]",". # Any string of 8-bit bytes may be encoded as [len]":"[string]",". Loading Loading @@ -188,6 +193,12 @@ class SocketmapsRequestHandler(socketserver.BaseRequestHandler): # matter, since Postfix internally processes lookup results as a # matter, since Postfix internally processes lookup results as a # comma-separated list. # comma-separated list. return 'OK ' + ','.join(results) return 'OK ' + ','.join(results) if name == 'remailer_canonical': domain = key.rsplit('@', 1)[-1] result = self.api.resolve_remailer(key) if result is None: return 'NOTFOUND ' return 'OK ' + result return 'PERM Unknown request name', logging.WARNING return 'PERM Unknown request name', logging.WARNING def make_requesthandler(api): def make_requesthandler(api): Loading