Compare commits
11 Commits
df24c92e54
...
eb7a22729a
Author | SHA1 | Date |
---|---|---|
Egor Savkin | eb7a22729a | |
Egor Savkin | a6a77f56e1 | |
Egor Savkin | d337612ccc | |
Egor Savkin | 3f3a1e62e6 | |
Egor Savkin | bc3f4608d5 | |
Egor Savkin | fbb6a9b570 | |
Egor Savkin | 4cbbb8d945 | |
Sébastien Bourdeauducq | 6dc8214102 | |
Sébastien Bourdeauducq | a6b216bb87 | |
Sébastien Bourdeauducq | 6e21a95ba8 | |
Sébastien Bourdeauducq | d08186a27a |
|
@ -0,0 +1,43 @@
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
upstream rfq_server {
|
||||||
|
server 127.0.0.1:5000;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
limit_conn addr 5;
|
||||||
|
|
||||||
|
root /var/www/m-labs-intl.com/html;
|
||||||
|
index index.html index.htm index.nginx-debian.html;
|
||||||
|
|
||||||
|
server_name m-labs-intl.com;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
listen [::]:443 ssl ipv6only=on; # managed by Certbot
|
||||||
|
listen 443 ssl; # managed by Certbot
|
||||||
|
ssl_certificate /etc/letsencrypt/live/m-labs-intl.com/fullchain.pem; # managed by Certbot
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/m-labs-intl.com/privkey.pem; # managed by Certbot
|
||||||
|
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||||
|
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
server_name www.m-labs-intl.com;
|
||||||
|
return 301 https://m-labs-intl.com$request_uri;
|
||||||
|
|
||||||
|
listen [::]:443 ssl; # managed by Certbot
|
||||||
|
listen 443 ssl; # managed by Certbot
|
||||||
|
ssl_certificate /etc/letsencrypt/live/m-labs-intl.com/fullchain.pem; # managed by Certbot
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/m-labs-intl.com/privkey.pem; # managed by Certbot
|
||||||
|
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||||
|
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
server_name hooks.m-labs-intl.com;
|
||||||
|
limit_conn addr 5;
|
||||||
|
|
||||||
|
location /rfq {
|
||||||
|
proxy_pass http://rfq_server/rfq;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_read_timeout 30;
|
||||||
|
proxy_connect_timeout 30;
|
||||||
|
proxy_send_timeout 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
return 418;
|
||||||
|
}
|
||||||
|
|
||||||
|
listen [::]:443 ssl; # managed by Certbot
|
||||||
|
listen 443 ssl; # managed by Certbot
|
||||||
|
ssl_certificate /etc/letsencrypt/live/m-labs-intl.com/fullchain.pem; # managed by Certbot
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/m-labs-intl.com/privkey.pem; # managed by Certbot
|
||||||
|
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||||
|
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
limit_conn addr 5;
|
||||||
|
if ($host = m-labs-intl.com) {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
} # managed by Certbot
|
||||||
|
|
||||||
|
|
||||||
|
if ($host = www.m-labs-intl.com) {
|
||||||
|
return 301 https://m-labs-intl.com$request_uri;
|
||||||
|
} # managed by Certbot
|
||||||
|
|
||||||
|
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
|
||||||
|
server_name m-labs-intl.com www.m-labs-intl.com hooks.m-labs-intl.com;
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
# 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 (Ubuntu)
|
||||||
|
biff = no
|
||||||
|
|
||||||
|
# appending .domain is the MUA's job.
|
||||||
|
append_dot_mydomain = no
|
||||||
|
|
||||||
|
# Uncomment the next line to generate "delayed mail" warnings
|
||||||
|
#delay_warning_time = 4h
|
||||||
|
|
||||||
|
readme_directory = no
|
||||||
|
|
||||||
|
# See http://www.postfix.org/COMPATIBILITY_README.html -- default to 3.6 on
|
||||||
|
# fresh installs.
|
||||||
|
compatibility_level = 3.6
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# TLS parameters
|
||||||
|
smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||||
|
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
|
||||||
|
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 = mail.m-labs-intl.com
|
||||||
|
mydomain = m-labs-intl.com
|
||||||
|
alias_maps = hash:/etc/aliases
|
||||||
|
alias_database = hash:/etc/aliases
|
||||||
|
myorigin = $mydomain
|
||||||
|
#mydestination = $myhostname, m-labs-intl.com, localhost.localdomain, localhost
|
||||||
|
mydestination =
|
||||||
|
relayhost = mail.m-labs.hk
|
||||||
|
#relay_domains = $mydomain
|
||||||
|
local_transport = error:local delivery disabled
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
virtual_alias_maps = hash:/etc/postfix/virtual
|
||||||
|
virtual_alias_domains = $mydomain m-labs.hk
|
||||||
|
|
||||||
|
|
||||||
|
# DKIM
|
||||||
|
milter_default_action = accept
|
||||||
|
milter_protocol = 2
|
||||||
|
smtpd_milters = inet:localhost:8891
|
||||||
|
non_smtpd_milters = inet:localhost:8891
|
|
@ -0,0 +1,90 @@
|
||||||
|
user www-data;
|
||||||
|
worker_processes auto;
|
||||||
|
pid /run/nginx.pid;
|
||||||
|
error_log /var/log/nginx/error.log;
|
||||||
|
include /etc/nginx/modules-enabled/*.conf;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 768;
|
||||||
|
# multi_accept on;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
|
||||||
|
##
|
||||||
|
# Basic Settings
|
||||||
|
##
|
||||||
|
|
||||||
|
sendfile on;
|
||||||
|
tcp_nopush on;
|
||||||
|
types_hash_max_size 2048;
|
||||||
|
# server_tokens off;
|
||||||
|
|
||||||
|
server_names_hash_bucket_size 64;
|
||||||
|
# server_name_in_redirect off;
|
||||||
|
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
##
|
||||||
|
# SSL Settings
|
||||||
|
##
|
||||||
|
|
||||||
|
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
|
||||||
|
ssl_prefer_server_ciphers on;
|
||||||
|
|
||||||
|
# Rate limiting
|
||||||
|
limit_conn_zone $binary_remote_addr zone=addr:10m;
|
||||||
|
|
||||||
|
##
|
||||||
|
# Logging Settings
|
||||||
|
##
|
||||||
|
|
||||||
|
access_log /var/log/nginx/access.log;
|
||||||
|
|
||||||
|
##
|
||||||
|
# Gzip Settings
|
||||||
|
##
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
|
||||||
|
# gzip_vary on;
|
||||||
|
# gzip_proxied any;
|
||||||
|
# gzip_comp_level 6;
|
||||||
|
# gzip_buffers 16 8k;
|
||||||
|
# gzip_http_version 1.1;
|
||||||
|
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||||||
|
|
||||||
|
##
|
||||||
|
# Virtual Host Configs
|
||||||
|
##
|
||||||
|
|
||||||
|
include /etc/nginx/conf.d/*.conf;
|
||||||
|
include /etc/nginx/sites-enabled/*;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
stream {
|
||||||
|
# Upstream mail servers
|
||||||
|
upstream smtp_backend {
|
||||||
|
server mail.m-labs.hk:25;
|
||||||
|
}
|
||||||
|
|
||||||
|
upstream submission_backend {
|
||||||
|
server mail.m-labs.hk:587;
|
||||||
|
}
|
||||||
|
|
||||||
|
# SMTP
|
||||||
|
server {
|
||||||
|
listen 25;
|
||||||
|
proxy_protocol on;
|
||||||
|
proxy_pass smtp_backend;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Submission (Authenticated SMTP)
|
||||||
|
server {
|
||||||
|
listen 587;
|
||||||
|
proxy_protocol on;
|
||||||
|
proxy_pass submission_backend;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
# NOTE: This is a legacy configuration file. It is not used by the opendkim
|
||||||
|
# systemd service. Please use the corresponding configuration parameters in
|
||||||
|
# /etc/opendkim.conf instead.
|
||||||
|
#
|
||||||
|
# Previously, one would edit the default settings here, and then execute
|
||||||
|
# /lib/opendkim/opendkim.service.generate to generate systemd override files at
|
||||||
|
# /etc/systemd/system/opendkim.service.d/override.conf and
|
||||||
|
# /etc/tmpfiles.d/opendkim.conf. While this is still possible, it is now
|
||||||
|
# recommended to adjust the settings directly in /etc/opendkim.conf.
|
||||||
|
#
|
||||||
|
#DAEMON_OPTS=""
|
||||||
|
# Change to /var/spool/postfix/run/opendkim to use a Unix socket with
|
||||||
|
# postfix in a chroot:
|
||||||
|
#RUNDIR=/var/spool/postfix/run/opendkim
|
||||||
|
RUNDIR=/run/opendkim
|
||||||
|
#
|
||||||
|
# Uncomment to specify an alternate socket
|
||||||
|
# Note that setting this will override any Socket value in opendkim.conf
|
||||||
|
# default:
|
||||||
|
SOCKET=local:$RUNDIR/opendkim.sock
|
||||||
|
# listen on all interfaces on port 54321:
|
||||||
|
#SOCKET=inet:54321
|
||||||
|
# listen on loopback on port 12345:
|
||||||
|
#SOCKET=inet:12345@localhost
|
||||||
|
# listen on 192.0.2.1 on port 12345:
|
||||||
|
#SOCKET=inet:12345@192.0.2.1
|
||||||
|
USER=opendkim
|
||||||
|
GROUP=opendkim
|
||||||
|
PIDFILE=$RUNDIR/$NAME.pid
|
||||||
|
EXTRAAFTER=
|
||||||
|
|
||||||
|
SOCKET="inet:8891@localhost"
|
|
@ -0,0 +1,57 @@
|
||||||
|
# This is a basic configuration for signing and verifying. It can easily be
|
||||||
|
# adapted to suit a basic installation. See opendkim.conf(5) and
|
||||||
|
# /usr/share/doc/opendkim/examples/opendkim.conf.sample for complete
|
||||||
|
# documentation of available configuration parameters.
|
||||||
|
|
||||||
|
Syslog yes
|
||||||
|
SyslogSuccess yes
|
||||||
|
#LogWhy no
|
||||||
|
|
||||||
|
# Common signing and verification parameters. In Debian, the "From" header is
|
||||||
|
# oversigned, because it is often the identity key used by reputation systems
|
||||||
|
# and thus somewhat security sensitive.
|
||||||
|
Canonicalization relaxed/simple
|
||||||
|
#Mode sv
|
||||||
|
#SubDomains no
|
||||||
|
OversignHeaders From
|
||||||
|
|
||||||
|
# Signing domain, selector, and key (required). For example, perform signing
|
||||||
|
# for domain "example.com" with selector "2020" (2020._domainkey.example.com),
|
||||||
|
# using the private key stored in /etc/dkimkeys/example.private. More granular
|
||||||
|
# setup options can be found in /usr/share/doc/opendkim/README.opendkim.
|
||||||
|
#Domain example.com
|
||||||
|
#Selector 2020
|
||||||
|
#KeyFile /etc/dkimkeys/example.private
|
||||||
|
|
||||||
|
# In Debian, opendkim runs as user "opendkim". A umask of 007 is required when
|
||||||
|
# using a local socket with MTAs that access the socket as a non-privileged
|
||||||
|
# user (for example, Postfix). You may need to add user "postfix" to group
|
||||||
|
# "opendkim" in that case.
|
||||||
|
UserID opendkim
|
||||||
|
UMask 007
|
||||||
|
|
||||||
|
# Socket for the MTA connection (required). If the MTA is inside a chroot jail,
|
||||||
|
# it must be ensured that the socket is accessible. In Debian, Postfix runs in
|
||||||
|
# a chroot in /var/spool/postfix, therefore a Unix socket would have to be
|
||||||
|
# configured as shown on the last line below.
|
||||||
|
Socket local:/run/opendkim/opendkim.sock
|
||||||
|
#Socket inet:8891@localhost
|
||||||
|
#Socket inet:8891
|
||||||
|
#Socket local:/var/spool/postfix/opendkim/opendkim.sock
|
||||||
|
|
||||||
|
PidFile /run/opendkim/opendkim.pid
|
||||||
|
|
||||||
|
# Hosts for which to sign rather than verify, default is 127.0.0.1. See the
|
||||||
|
# OPERATION section of opendkim(8) for more information.
|
||||||
|
#InternalHosts 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12
|
||||||
|
|
||||||
|
# The trust anchor enables DNSSEC. In Debian, the trust anchor file is provided
|
||||||
|
# by the package dns-root-data.
|
||||||
|
TrustAnchorFile /usr/share/dns/root.key
|
||||||
|
#Nameservers 127.0.0.1
|
||||||
|
|
||||||
|
|
||||||
|
Domain m-labs-intl.com
|
||||||
|
KeyFile /etc/postfix/dkim.key
|
||||||
|
Selector mail
|
||||||
|
SOCKET inet:8891@localhost
|
|
@ -0,0 +1,12 @@
|
||||||
|
[Unit]
|
||||||
|
Description=RFQ service
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=rfqserver
|
||||||
|
ExecStart=/home/rfqserver/runrfq.sh
|
||||||
|
Restart=on-failure
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
export FLASK_DEBUG=0
|
||||||
|
export FLASK_MAIL_SERVER=mail.m-labs.hk
|
||||||
|
export FLASK_MAIL_PORT=465
|
||||||
|
export FLASK_MAIL_USE_SSL=True
|
||||||
|
export FLASK_MAIL_USERNAME=sysop-intl@m-labs-intl.com
|
||||||
|
export FLASK_MAIL_PASSWORD_FILE=/home/rfqserver/mail.secret
|
||||||
|
export FLASK_MAIL_RECIPIENT=sales@m-labs.hk
|
||||||
|
export FLASK_MAIL_SENDER=sysop-intl@m-labs-intl.com
|
||||||
|
|
||||||
|
cd /home/rfqserver/web2019/server
|
||||||
|
source venv/bin/activate
|
||||||
|
python3 -m flask --app rfq run --port=5000
|
|
@ -0,0 +1,71 @@
|
||||||
|
# Setup m-labs-intl.com server
|
||||||
|
|
||||||
|
```shell
|
||||||
|
apt install git nginx-full python3 python3.12-venv python3-pip dante-server
|
||||||
|
snap install --classic certbot
|
||||||
|
ln -s /snap/bin/certbot /usr/bin/certbot
|
||||||
|
useradd -m rfqserver
|
||||||
|
useradd -m zolaupd
|
||||||
|
|
||||||
|
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/
|
||||||
|
|
||||||
|
cp runrfq.sh /home/rfqserver/
|
||||||
|
cp mail.secret /home/rfqserver/
|
||||||
|
chown rfqserver /home/rfqserver/runrfq.sh
|
||||||
|
chmod +x /home/rfqserver/runrfq.sh
|
||||||
|
chown rfqserver /home/rfqserver/mail.secret
|
||||||
|
|
||||||
|
|
||||||
|
sudo -u zolaupd sh -c '
|
||||||
|
cd /home/zolaupd;
|
||||||
|
mkdir /home/zolaupd/.ssh;
|
||||||
|
echo -n "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP1OJJM8g/1ffxDjN31XKEfGmrYaW03lwpyTa1UGWqVx
|
||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF6R6XK0IiuAKxVKvSABm4m9bfOlvfJcMvTpjenuXUPv" > /home/zolaupd/.ssh/authorized_keys
|
||||||
|
chmod 700 .ssh/
|
||||||
|
chmod 600 .ssh/authorized_keys
|
||||||
|
'
|
||||||
|
|
||||||
|
sudo -u rfqserver sh -c '
|
||||||
|
cd /home/rfqserver;
|
||||||
|
git clone https://git.m-labs.hk/M-Labs/web2019.git;
|
||||||
|
cd web2019;
|
||||||
|
python3 -m venv ./venv;
|
||||||
|
source venv/bin/activate;
|
||||||
|
pip install -r requirements.txt;
|
||||||
|
'
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
service nginx restart
|
||||||
|
|
||||||
|
ufw default deny
|
||||||
|
ufw allow from 94.190.212.123
|
||||||
|
ufw allow from 2001:470:f891:1:5999:5529:5d:f71d
|
||||||
|
ufw allow from 202.77.7.238
|
||||||
|
ufw allow from 2001:470:18:390::2
|
||||||
|
ufw allow "Nginx HTTP"
|
||||||
|
ufw allow "Nginx HTTPS"
|
||||||
|
ufw limit OpenSSH
|
||||||
|
ufw default allow outgoing
|
||||||
|
ufw limit 25/tcp
|
||||||
|
ufw limit 587/tcp
|
||||||
|
ufw show added
|
||||||
|
ufw enable
|
||||||
|
```
|
|
@ -0,0 +1,278 @@
|
||||||
|
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()
|
|
@ -0,0 +1 @@
|
||||||
|
es@m-labs-intl.com es@m-labs.hk
|
|
@ -26,9 +26,10 @@ let
|
||||||
${config.services.mysql.package}/bin/mysqldump --user=root --single-transaction flarum > flarum.sql
|
${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 mattermost > mattermost.sql
|
||||||
${config.services.postgresql.package}/bin/pg_dump rt5 > rt.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
|
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 | \
|
${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.bzip2}/bin/bzip2 | \
|
${pkgs.bzip2}/bin/bzip2 | \
|
||||||
${pkgs.gnupg}/bin/gpg --symmetric --batch --passphrase-fd 6
|
${pkgs.gnupg}/bin/gpg --symmetric --batch --passphrase-fd 6
|
||||||
'';
|
'';
|
||||||
|
|
|
@ -375,10 +375,14 @@ in
|
||||||
notify explicit;
|
notify explicit;
|
||||||
also-notify {
|
also-notify {
|
||||||
216.218.130.2; # ns1.he.net
|
216.218.130.2; # ns1.he.net
|
||||||
|
213.239.220.50; # ns1.qnetp.net
|
||||||
|
88.198.32.245; # new qnetp
|
||||||
};
|
};
|
||||||
'';
|
'';
|
||||||
slaves = [
|
slaves = [
|
||||||
"216.218.133.2" "2001:470:600::2" # slave.dns.he.net
|
"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" = {
|
"200-29.98.206.103.in-addr.arpa" = {
|
||||||
|
@ -773,6 +777,10 @@ in
|
||||||
services.gitea = {
|
services.gitea = {
|
||||||
enable = true;
|
enable = true;
|
||||||
appName = "M-Labs Git";
|
appName = "M-Labs Git";
|
||||||
|
database = {
|
||||||
|
type = "postgres";
|
||||||
|
socket = "/run/postgresql";
|
||||||
|
};
|
||||||
mailerPasswordFile = "/etc/nixos/secret/mailerpassword";
|
mailerPasswordFile = "/etc/nixos/secret/mailerpassword";
|
||||||
settings = {
|
settings = {
|
||||||
server = {
|
server = {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
$TTL 7200
|
$TTL 7200
|
||||||
|
|
||||||
@ SOA ns.m-labs-intl.com. sb.m-labs.hk. (
|
@ SOA ns.m-labs-intl.com. sb.m-labs.hk. (
|
||||||
2024081402
|
2024081503
|
||||||
7200
|
7200
|
||||||
3600
|
3600
|
||||||
86400
|
86400
|
||||||
|
@ -10,12 +10,14 @@ $TTL 7200
|
||||||
|
|
||||||
NS ns.m-labs-intl.com.
|
NS ns.m-labs-intl.com.
|
||||||
NS ns1.he.net.
|
NS ns1.he.net.
|
||||||
|
NS ns1.qnetp.net.
|
||||||
|
|
||||||
A 5.78.86.156
|
A 5.78.86.156
|
||||||
AAAA 2a01:4ff:1f0:83de::1
|
AAAA 2a01:4ff:1f0:83de::1
|
||||||
MX 10 mail.m-labs-intl.com.
|
MX 10 mail.m-labs-intl.com.
|
||||||
TXT "v=spf1 mx -all"
|
TXT "v=spf1 mx -all"
|
||||||
TXT "google-site-verification=BlQd5_5wWW7calKC7bZA0GdoxR8-zj4gwJEg9sGJ3l8"
|
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 A 94.190.212.123
|
||||||
ns AAAA 2001:470:18:390::2
|
ns AAAA 2001:470:18:390::2
|
||||||
|
|
Loading…
Reference in New Issue