mirror of
https://github.com/chatmail/relay.git
synced 2026-05-10 16:04:37 +00:00
Compare commits
2 Commits
traefik-su
...
cmdeploy-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5399ea1f59 | ||
|
|
f7d0a9150d |
87
README.md
87
README.md
@@ -456,94 +456,15 @@ to send messages outside.
|
||||
|
||||
To setup a reverse proxy
|
||||
(or rather Destination NAT, DNAT)
|
||||
for your chatmail relay,
|
||||
put the following configuration in `/etc/nftables.conf`:
|
||||
```
|
||||
#!/usr/sbin/nft -f
|
||||
|
||||
flush ruleset
|
||||
|
||||
define wan = eth0
|
||||
|
||||
# Which ports to proxy.
|
||||
#
|
||||
# Note that SSH is not proxied
|
||||
# so it is possible to log into the proxy server
|
||||
# and not the original one.
|
||||
define ports = { smtp, http, https, imap, imaps, submission, submissions }
|
||||
|
||||
# The host we want to proxy to.
|
||||
define ipv4_address = AAA.BBB.CCC.DDD
|
||||
define ipv6_address = [XXX::1]
|
||||
|
||||
table ip nat {
|
||||
chain prerouting {
|
||||
type nat hook prerouting priority dstnat; policy accept;
|
||||
iif $wan tcp dport $ports dnat to $ipv4_address
|
||||
}
|
||||
|
||||
chain postrouting {
|
||||
type nat hook postrouting priority 0;
|
||||
|
||||
oifname $wan masquerade
|
||||
}
|
||||
}
|
||||
|
||||
table ip6 nat {
|
||||
chain prerouting {
|
||||
type nat hook prerouting priority dstnat; policy accept;
|
||||
iif $wan tcp dport $ports dnat to $ipv6_address
|
||||
}
|
||||
|
||||
chain postrouting {
|
||||
type nat hook postrouting priority 0;
|
||||
|
||||
oifname $wan masquerade
|
||||
}
|
||||
}
|
||||
|
||||
table inet filter {
|
||||
chain input {
|
||||
type filter hook input priority filter; policy drop;
|
||||
|
||||
# Accept ICMP.
|
||||
# It is especially important to accept ICMPv6 ND messages,
|
||||
# otherwise IPv6 connectivity breaks.
|
||||
icmp type { echo-request } accept
|
||||
icmpv6 type { echo-request, nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept
|
||||
|
||||
# Allow incoming SSH connections.
|
||||
tcp dport { ssh } accept
|
||||
|
||||
ct state established accept
|
||||
}
|
||||
chain forward {
|
||||
type filter hook forward priority filter; policy drop;
|
||||
|
||||
ct state established accept
|
||||
ip daddr $ipv4_address counter accept
|
||||
ip6 daddr $ipv6_address counter accept
|
||||
}
|
||||
chain output {
|
||||
type filter hook output priority filter;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Run `systemctl enable nftables.service`
|
||||
to ensure configuration is reloaded when the proxy relay reboots.
|
||||
|
||||
Uncomment in `/etc/sysctl.conf` the following two lines:
|
||||
for your chatmail relay, run:
|
||||
|
||||
```
|
||||
net.ipv4.ip_forward=1
|
||||
net.ipv6.conf.all.forwarding=1
|
||||
scripts/cmdeploy proxy <proxy_ip_address> --relay-ipv4 <relay_ipv4_address> --relay-ipv6 <relay_ipv6_address>
|
||||
```
|
||||
|
||||
Then reboot the relay or do `sysctl -p` and `nft -f /etc/nftables.conf`.
|
||||
|
||||
Once proxy relay is set up,
|
||||
you can add its IP address to the DNS.
|
||||
you can add its IP address to the DNS,
|
||||
or distribute it as you wish.
|
||||
|
||||
## Neighbors and Acquaintances
|
||||
|
||||
|
||||
@@ -208,6 +208,61 @@ def test_cmd(args, out):
|
||||
return ret
|
||||
|
||||
|
||||
def proxy_cmd_options(parser: argparse.ArgumentParser):
|
||||
parser.add_argument(
|
||||
"ip_address",
|
||||
help="specify a server to deploy to; can also be an inventory.py file",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--relay-ipv4",
|
||||
dest="relay_ipv4",
|
||||
help="The ipv4 address of the relay you want to forward traffic to",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--relay-ipv6",
|
||||
dest="relay_ipv6",
|
||||
help="The ipv6 address of the relay you want to forward traffic to",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dry-run",
|
||||
dest="dry_run",
|
||||
action="store_true",
|
||||
help="don't actually modify the server",
|
||||
)
|
||||
|
||||
|
||||
def proxy_cmd(args, out):
|
||||
"""Deploy reverse proxy on a second server."""
|
||||
env = os.environ.copy()
|
||||
env["RELAY_IPV4"] = args.relay_ipv4
|
||||
env["RELAY_IPV6"] = args.relay_ipv6
|
||||
deploy_path = importlib.resources.files(__package__).joinpath("proxy-deploy.py").resolve()
|
||||
pyinf = "pyinfra --dry" if args.dry_run else "pyinfra"
|
||||
|
||||
sshexec = args.get_sshexec()
|
||||
# :todo make sure relay is deployed to args.relay_ipv4 and args.relay_ipv6
|
||||
|
||||
# abort if IP address == the chatmail relay itself: if port 22 is open AND /etc/chatmail-version exists
|
||||
if sshexec.logged(call=remote.rshell.get_port_service, args=[22]):
|
||||
if sshexec.logged(call=remote.rshell.chatmail_version):
|
||||
out.red("Can not deploy proxy on the chatmail relay itself, use another server")
|
||||
return 1
|
||||
cmd = f"{pyinf} --ssh-user root {args.ip_address} {deploy_path} -y"
|
||||
out.check_call(cmd, env=env) # during first try, only set SSH port to 2222
|
||||
|
||||
cmd = f"{pyinf} --ssh-port 2222 --ssh-user root {args.ip_address} {deploy_path} -y"
|
||||
try:
|
||||
retcode = out.check_call(cmd, env=env)
|
||||
if retcode == 0:
|
||||
out.green("Reverse proxy deployed - you can distribute the IP address now.")
|
||||
else:
|
||||
out.red("Deploying reverse proxy failed")
|
||||
except subprocess.CalledProcessError:
|
||||
out.red("Deploying reverse proxy failed")
|
||||
retcode = 1
|
||||
return retcode
|
||||
|
||||
|
||||
def fmt_cmd_options(parser):
|
||||
parser.add_argument(
|
||||
"--check",
|
||||
|
||||
19
cmdeploy/src/cmdeploy/proxy-deploy.py
Normal file
19
cmdeploy/src/cmdeploy/proxy-deploy.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import os
|
||||
|
||||
import pyinfra
|
||||
from pyinfra import host
|
||||
|
||||
from proxy import configure_ssh, configure_proxy
|
||||
|
||||
|
||||
def main():
|
||||
ipv4_relay = os.getenv("IPV4_RELAY")
|
||||
ipv6_relay = os.getenv("IPV6_RELAY")
|
||||
|
||||
configure_ssh()
|
||||
if host.data.get("ssh_port") not in (None, 22):
|
||||
configure_proxy(ipv4_relay, ipv6_relay)
|
||||
|
||||
|
||||
if pyinfra.is_cli:
|
||||
main()
|
||||
63
cmdeploy/src/cmdeploy/proxy.py
Normal file
63
cmdeploy/src/cmdeploy/proxy.py
Normal file
@@ -0,0 +1,63 @@
|
||||
import importlib
|
||||
|
||||
from pyinfra import host
|
||||
from pyinfra.operations import files, server, apt, systemd
|
||||
|
||||
def configure_ssh():
|
||||
files.replace(
|
||||
name="Configure sshd to use port 2222",
|
||||
path="/etc/ssh/sshd_config",
|
||||
text="Port 22\n",
|
||||
replace="Port 2222\n",
|
||||
)
|
||||
systemd.service(
|
||||
name="apply SSH config",
|
||||
service="ssh",
|
||||
reloaded=True,
|
||||
)
|
||||
apt.update()
|
||||
|
||||
|
||||
def configure_proxy(ipv4_relay, ipv6_relay):
|
||||
files.put(
|
||||
name="Configure nftables",
|
||||
src=importlib.resources.files(__package__).joinpath("proxy_files/nftables.conf.j2"),
|
||||
dest="/etc/nftables.conf",
|
||||
ipv4_address=ipv4_relay, # :todo what if only one of them is specified?
|
||||
ipv6_address=ipv6_relay,
|
||||
)
|
||||
|
||||
server.sysctl(name="enable IPv4 forwarding", key="net.ipv4.ip_forward", value=1, persist=True)
|
||||
|
||||
server.sysctl(
|
||||
name="enable IPv6 forwarding",
|
||||
key="net.ipv6.conf.all.forwarding",
|
||||
value=1,
|
||||
persist=True,
|
||||
)
|
||||
|
||||
server.shell(
|
||||
name="apply forwarding configuration",
|
||||
commands=[
|
||||
"sysctl -p",
|
||||
"nft -f /etc/nftables.conf",
|
||||
],
|
||||
)
|
||||
|
||||
if host.data.get("floating_ips"):
|
||||
i = 0
|
||||
for floating_ip in host.data.get("floating_ips"):
|
||||
i += 1
|
||||
files.template(
|
||||
name="Add floating IPs",
|
||||
src="servers/proxy-nine/files/60-floating.ip.cfg.j2",
|
||||
dest=f"/etc/network/interfaces.d/{59 + i}-floating.ip.cfg",
|
||||
ip_address=floating_ip,
|
||||
i=i,
|
||||
)
|
||||
|
||||
systemd.service(
|
||||
name="apply floating IPs",
|
||||
service="networking",
|
||||
restarted=True,
|
||||
)
|
||||
4
cmdeploy/src/cmdeploy/proxy_files/60-floating.ip.cfg.j2
Normal file
4
cmdeploy/src/cmdeploy/proxy_files/60-floating.ip.cfg.j2
Normal file
@@ -0,0 +1,4 @@
|
||||
auto eth0:{{ i }}
|
||||
iface eth0:{{ i }} inet static
|
||||
address {{ ip_address }}
|
||||
netmask 32
|
||||
67
cmdeploy/src/cmdeploy/proxy_files/nftables.conf.j2
Normal file
67
cmdeploy/src/cmdeploy/proxy_files/nftables.conf.j2
Normal file
@@ -0,0 +1,67 @@
|
||||
#!/usr/sbin/nft -f
|
||||
|
||||
flush ruleset
|
||||
|
||||
define wan = eth0
|
||||
|
||||
# which ports to proxy
|
||||
define ports = { smtp, http, https, imap, imaps, submission, submissions }
|
||||
|
||||
# the host we want to proxy to
|
||||
define ipv4_address = {{ ipv4_address }}
|
||||
define ipv6_address = [{{ ipv6_address }}]
|
||||
|
||||
table ip nat {
|
||||
chain prerouting {
|
||||
type nat hook prerouting priority dstnat; policy accept;
|
||||
iif $wan tcp dport $ports dnat to $ipv4_address
|
||||
}
|
||||
|
||||
chain postrouting {
|
||||
type nat hook postrouting priority 0;
|
||||
|
||||
oifname $wan masquerade
|
||||
}
|
||||
}
|
||||
|
||||
table ip6 nat {
|
||||
chain prerouting {
|
||||
type nat hook prerouting priority dstnat; policy accept;
|
||||
iif $wan tcp dport $ports dnat to $ipv6_address
|
||||
}
|
||||
|
||||
chain postrouting {
|
||||
type nat hook postrouting priority 0;
|
||||
|
||||
oifname $wan masquerade
|
||||
}
|
||||
}
|
||||
|
||||
table inet filter {
|
||||
chain input {
|
||||
type filter hook input priority filter; policy drop;
|
||||
|
||||
# Accept ICMP.
|
||||
# It is especially important to accept ICMPv6 ND messages,
|
||||
# otherwise IPv6 connectivity breaks.
|
||||
icmp type { echo-request } accept
|
||||
icmpv6 type { echo-request, nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept
|
||||
|
||||
# Allow incoming SSH connections.
|
||||
tcp dport { 22, 2222 } accept
|
||||
# Allow incoming shadowsocks connections.
|
||||
tcp dport { 8388 } accept
|
||||
|
||||
ct state established accept
|
||||
}
|
||||
chain forward {
|
||||
type filter hook forward priority filter; policy drop;
|
||||
|
||||
ct state established accept
|
||||
ip daddr $ipv4_address counter accept
|
||||
ip6 daddr $ipv6_address counter accept
|
||||
}
|
||||
chain output {
|
||||
type filter hook output priority filter;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,20 @@ def shell(command, fail_ok=False, print=print):
|
||||
return ""
|
||||
|
||||
|
||||
def get_port_service(port: int) -> str:
|
||||
return shell(
|
||||
"ss -lptn 'src :%d' | awk 'NR>1 {print $6,$7}' | sed 's/users:((\"//;s/\".*//'"
|
||||
% (port,)
|
||||
)
|
||||
|
||||
|
||||
def chatmail_version():
|
||||
version = shell("cat /etc/chatmail-version")
|
||||
if "cat: /etc/chatmail-version:" in version:
|
||||
version = None
|
||||
return version
|
||||
|
||||
|
||||
def get_systemd_running():
|
||||
lines = shell("systemctl --type=service --state=running").split("\n")
|
||||
return [line for line in lines if line.startswith(" ")]
|
||||
|
||||
Reference in New Issue
Block a user