Compare commits

..

6 Commits

Author SHA1 Message Date
Egor Savkin df24c92e54 Remove request rate restriction and remove proxy protocol
Signed-off-by: Egor Savkin <es@m-labs.hk>
2024-08-16 17:50:13 +08:00
Egor Savkin 4bbf162a87 Limit connections and redirect www to canonical
Signed-off-by: Egor Savkin <es@m-labs.hk>
2024-08-15 13:09:00 +08:00
Egor Savkin 164c0f021c Move away from automated script
Signed-off-by: Egor Savkin <es@m-labs.hk>
2024-08-14 11:50:33 +08:00
Egor Savkin e60896c288 Postfix instead of nginx streams
Signed-off-by: Egor Savkin <es@m-labs.hk>
2024-08-14 11:50:33 +08:00
Egor Savkin f9985dc2c1 Remove imap and pop3 ports proxy
Signed-off-by: Egor Savkin <es@m-labs.hk>
2024-08-14 11:50:33 +08:00
Egor Savkin 8355672611 Add configuration for the m-labs-intl
Signed-off-by: Egor Savkin <es@m-labs.hk>
2024-08-14 11:50:33 +08:00
7 changed files with 3 additions and 341 deletions

View File

@ -1,43 +0,0 @@
logoutput: syslog
user.privileged: root
user.unprivileged: nobody
# The listening network interface or address.
internal: 5.78.86.156 port=2025
internal: 2a01:4ff:1f0:83de::1 port = 2025
# The proxying network interface or address.
external: eth0
# socks-rules determine what is proxied through the external interface.
socksmethod: none
# client-rules determine who can connect to the internal interface.
clientmethod: none
client pass {
from: 94.190.212.123/32 to: 0.0.0.0/0
}
socks pass {
from: 94.190.212.123/32 to: 0.0.0.0/0
}
client pass {
from: 202.77.7.238/32 to: 0.0.0.0/0
}
socks pass {
from: 202.77.7.238/32 to: 0.0.0.0/0
}
client pass {
from: 2001:470:18:390::2/128 to: 0.0.0.0/0
}
socks pass {
from: 2001:470:18:390::2/128 to: 0.0.0.0/0
}
client pass {
from: 2001:470:f891:1:5999:5529:5d:f71d/128 to: 0.0.0.0/0
}
socks pass {
from: 2001:470:f891:1:5999:5529:5d:f71d/128 to: 0.0.0.0/0
}

View File

@ -77,14 +77,12 @@ stream {
# SMTP
server {
listen 25;
proxy_protocol on;
proxy_pass smtp_backend;
}
# Submission (Authenticated SMTP)
server {
listen 587;
proxy_protocol on;
proxy_pass submission_backend;
}
}

View File

@ -1,7 +1,7 @@
# Setup m-labs-intl.com server
```shell
apt install git nginx-full python3 python3.12-venv python3-pip dante-server
apt install git nginx-full python3 python3.12-venv python3-pip
snap install --classic certbot
ln -s /snap/bin/certbot /usr/bin/certbot
useradd -m rfqserver
@ -11,8 +11,6 @@ cp m-labs-intl.com /etc/nginx/sites-available/
cp nginx.conf /etc/nginx/
ln -s /etc/nginx/sites-available/m-labs-intl.com /etc/nginx/sites-enabled/
cp danted.conf /etc/
mkdir -p /var/www/m-labs-intl.com/html
chown -R zolaupd /var/www/m-labs-intl.com/
@ -46,10 +44,8 @@ cp rfq.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable rfq.service
systemctl start rfq.service
systemctl enable danted.service
service nginx restart
service danted restart
certbot --nginx

View File

@ -1,278 +0,0 @@
import asyncio
import email
import logging
from smtplib import SMTP as SMTPClient
from typing import Dict
from aiosmtpd.proxy_protocol import ProxyData
from python_socks.sync import Proxy
import os
import ssl
from aiosmtpd.controller import Controller
from aiosmtpd.smtp import Envelope, Session, SMTP
from email.message import Message
import dkim
import spf
from functools import lru_cache
import dns.resolver
PROXY = Proxy.from_url('socks5://5.78.86.156:2025')
END_HOST = "localhost"
END_PORT = 25
PROXY_SMTP_HOST = "*"
PROXY_SMTP_PORT = 2025
END_SMTP_HOST = "*"
END_SMTP_PORT = 3025
GENERIC_SMTP_HOST = "*"
GENERIC_SMTP_PORT = 25
log = logging.getLogger("smtphandler")
VALID_PROXY_ADDRS = {"5.78.86.156", "2a01:4ff:1f0:83de::1", "m-labs-intl.com", "mail.m-labs-intl.com"}
VALID_END_ADDRS = {"localhost", "127.0.0.1"}
VALID_RECEPIENTS = {"m-labs.hk", "m-labs.ph", "m-labs-intl.com", "193thz.com", "malloctech.fr"}
@lru_cache(maxsize=256)
def get_mx(domain):
records = dns.resolver.resolve(domain, "MX")
if not records:
return None
result = max(records, key=lambda r: r.preference)
return str(result.exchange)
class ProxiedSMTP(SMTPClient):
def _get_socket(self, host, port, timeout):
return PROXY.connect(dest_host=host, dest_port=port, timeout=timeout)
class GenericMailServer:
"""
Accepts mail from everywhere, and passes it to the real SMTP server, after proper SPF and DKIM checks.
"""
async def handle_MAIL(self,
server: SMTP,
session: Session,
envelope: Envelope,
address: str,
mail_options: list):
ip = session.peer[0]
result, description = spf.check2(ip, address, session.host_name)
valid_spf = result == 'pass'
envelope.spf = valid_spf
log.info("SPF: %s, %s", result, description)
if not valid_spf:
return '550 SPF validation failed'
envelope.mail_from = address
envelope.mail_options.extend(mail_options)
return '250 OK'
async def handle_RCPT(self,
server: SMTP,
session: Session,
envelope: Envelope,
address: str,
rcpt_options: list):
if address.split("@")[1] not in VALID_RECEPIENTS:
return '550 not relaying to that domain'
log.debug("Handle RCPT for %s", address)
envelope.rcpt_tos.append(address)
return '250 OK'
async def handle_DATA(self,
server: SMTP,
session: Session,
envelope: Envelope):
valid_dkim = dkim.verify(envelope.content)
envelope.dkim = valid_dkim
log.info("DKIM: %s", valid_dkim)
message: Message = email.message_from_bytes(envelope.content)
if not valid_dkim:
return '550 DKIM validation failed'
log.info('Message: %s', message)
try:
with SMTPClient(END_HOST, END_PORT) as client:
client.sendmail(
from_addr=envelope.mail_from,
to_addrs=envelope.rcpt_tos,
msg=envelope.original_content
)
except BaseException as e:
print(e)
return '500 Could not process your message'
return '250 Message accepted for delivery'
class EndMailServer:
"""
Accepts mail only from end server. Checks if mail is signed to be from proxy, and sends to proxy if needed.
SPF and DKIM needs to be already included.
"""
async def handle_DATA(self,
server: SMTP,
session: Session,
envelope: Envelope):
ip = session.peer[0]
if ip not in VALID_END_ADDRS:
return "521 Server doesn't accept mail"
message: Message = email.message_from_bytes(envelope.content)
log.info('Message: %s', message)
mx_rcpt: Dict[str, list[str]] = {}
for rcpt in envelope.rcpt_tos:
_, _, domain = rcpt.partition("@")
mx = get_mx(domain)
if mx is None:
continue
mx_rcpt.setdefault(mx, []).append(rcpt)
try:
for mx, rcpts in mx_rcpt.items():
if envelope.mail_from in VALID_PROXY_ADDRS:
with ProxiedSMTP(mx, 25) as client:
client.sendmail(
from_addr=envelope.mail_from,
to_addrs=rcpts,
msg=envelope.original_content
)
else:
with SMTPClient(mx, 25) as client:
client.sendmail(
from_addr=envelope.mail_from,
to_addrs=rcpts,
msg=envelope.original_content
)
except BaseException as e:
print(e)
return '500 Could not process your message'
return '250 Message accepted for delivery'
class ProxyMailServer:
"""
Accepts mail only from proxy server, and passes it to the real SMTP server, after proper SPF and DKIM checks.
"""
async def handle_PROXY(self, server: SMTP, session: Session, envelope: Envelope, proxy_data: ProxyData):
ip = session.peer[0]
envelope.proxy_data = proxy_data
return ip in VALID_PROXY_ADDRS
async def handle_MAIL(self,
server: SMTP,
session: Session,
envelope: Envelope,
address: str,
mail_options: list):
ip = envelope.proxy_data.src_addr
result, description = spf.check2(ip, address, session.host_name)
valid_spf = result == 'pass'
envelope.spf = valid_spf
log.info("SPF: %s, %s", result, description)
if not valid_spf:
return '550 SPF validation failed'
envelope.mail_from = address
envelope.mail_options.extend(mail_options)
return '250 OK'
async def handle_RCPT(self,
server: SMTP,
session: Session,
envelope: Envelope,
address: str,
rcpt_options: list):
if address.split("@")[1] not in VALID_RECEPIENTS:
return '550 not relaying to that domain'
log.debug("Handle RCPT for %s", address)
envelope.rcpt_tos.append(address)
return '250 OK'
async def handle_DATA(self,
server: SMTP,
session: Session,
envelope: Envelope):
valid_dkim = dkim.verify(envelope.content)
envelope.dkim = valid_dkim
log.info("DKIM: %s", valid_dkim)
message: Message = email.message_from_bytes(envelope.content)
if not valid_dkim:
return '550 DKIM validation failed'
log.info('Message: %s', message)
try:
with SMTPClient(END_HOST, END_PORT) as client:
client.sendmail(
from_addr=envelope.mail_from,
to_addrs=envelope.rcpt_tos,
msg=envelope.original_content
)
except BaseException as e:
print(e)
return '500 Could not process your message'
return '250 Message accepted for delivery'
if __name__ == '__main__':
host = os.getenv('SMTP_HOST', '*')
port = int(os.getenv('SMTP_PORT', '25'))
accept_host = os.getenv('ACCEPT_HOST')
ssl_keys = os.getenv('SSL_KEYS')
loop = asyncio.get_event_loop()
end_handler = EndMailServer()
proxy_handler = ProxyMailServer()
generic_handler = GenericMailServer()
ssl_context = None
if ssl_keys:
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_context.load_cert_chain(ssl_keys + '.crt', ssl_keys + '.key')
generic_controller = Controller(GenericMailServer, hostname=GENERIC_SMTP_HOST, port=GENERIC_SMTP_PORT)
generic_controller.factory = lambda: SMTP(GenericMailServer, enable_SMTPUTF8=True, tls_context=ssl_context)
generic_controller.start()
log.info("Generic SMTP server started on %s:%s", GENERIC_SMTP_HOST, GENERIC_SMTP_PORT)
end_controller = Controller(EndMailServer, hostname=END_SMTP_HOST, port=END_SMTP_PORT)
end_controller.factory = lambda: SMTP(EndMailServer, enable_SMTPUTF8=True)
end_controller.start()
log.info("End SMTP server started on %s:%s", END_SMTP_HOST, END_SMTP_PORT)
proxy_controller = Controller(ProxyMailServer, hostname=PROXY_SMTP_HOST, port=PROXY_SMTP_PORT)
proxy_controller.factory = lambda: SMTP(ProxyMailServer, enable_SMTPUTF8=True, tls_context=ssl_context)
proxy_controller.start()
log.info("Proxy SMTP server started on %s:%s", PROXY_SMTP_HOST, PROXY_SMTP_PORT)
try:
loop.run_forever()
except KeyboardInterrupt:
print("Shutting down")
finally:
generic_controller.stop()
end_controller.stop()
proxy_controller.stop()
loop.stop()
loop.close()

View File

@ -26,10 +26,9 @@ let
${config.services.mysql.package}/bin/mysqldump --user=root --single-transaction flarum > flarum.sql
${config.services.postgresql.package}/bin/pg_dump mattermost > mattermost.sql
${config.services.postgresql.package}/bin/pg_dump rt5 > rt.sql
${config.services.postgresql.package}/bin/pg_dump gitea > gitea.sql
exec 6< /etc/nixos/secret/backup-passphrase
${pkgs.gnutar}/bin/tar cf - ${lib.concatMapStringsSep " " (p: "--exclude \"${p}\"") excludePaths} /etc/nixos /var/vmail /var/lib/hedgedoc /var/lib/gitea /var/lib/afws /var/lib/mattermost/data /var/www/193thz flarum.sql mattermost.sql rt.sql gitea.sql | \
${pkgs.gnutar}/bin/tar cf - ${lib.concatMapStringsSep " " (p: "--exclude \"${p}\"") excludePaths} /etc/nixos /var/vmail /var/lib/hedgedoc /var/lib/gitea /var/lib/afws /var/lib/mattermost/data /var/www/193thz flarum.sql mattermost.sql rt.sql | \
${pkgs.bzip2}/bin/bzip2 | \
${pkgs.gnupg}/bin/gpg --symmetric --batch --passphrase-fd 6
'';

View File

@ -375,14 +375,10 @@ in
notify explicit;
also-notify {
216.218.130.2; # ns1.he.net
213.239.220.50; # ns1.qnetp.net
88.198.32.245; # new qnetp
};
'';
slaves = [
"216.218.133.2" "2001:470:600::2" # slave.dns.he.net
"213.239.220.50" "2a01:4f8:a0:7041::1" # ns1.qnetp.net
"88.198.32.245" # new qnetp
];
};
"200-29.98.206.103.in-addr.arpa" = {
@ -777,10 +773,6 @@ in
services.gitea = {
enable = true;
appName = "M-Labs Git";
database = {
type = "postgres";
socket = "/run/postgresql";
};
mailerPasswordFile = "/etc/nixos/secret/mailerpassword";
settings = {
server = {

View File

@ -1,7 +1,7 @@
$TTL 7200
@ SOA ns.m-labs-intl.com. sb.m-labs.hk. (
2024081503
2024081402
7200
3600
86400
@ -10,14 +10,12 @@ $TTL 7200
NS ns.m-labs-intl.com.
NS ns1.he.net.
NS ns1.qnetp.net.
A 5.78.86.156
AAAA 2a01:4ff:1f0:83de::1
MX 10 mail.m-labs-intl.com.
TXT "v=spf1 mx -all"
TXT "google-site-verification=BlQd5_5wWW7calKC7bZA0GdoxR8-zj4gwJEg9sGJ3l8"
CAA 0 issue "letsencrypt.org; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/1768317117"
ns A 94.190.212.123
ns AAAA 2001:470:18:390::2