mirror of
https://github.com/chatmail/relay.git
synced 2026-05-19 20:38:05 +00:00
fix: change config to work also on debian 11
This commit is contained in:
1
.python-version
Normal file
1
.python-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
3.11
|
||||||
@@ -108,6 +108,13 @@ class AcmetoolDeployer(Deployer):
|
|||||||
self.need_restart_reconcile_timer = reconcile_timer_file.changed
|
self.need_restart_reconcile_timer = reconcile_timer_file.changed
|
||||||
|
|
||||||
def activate(self):
|
def activate(self):
|
||||||
|
systemd.service(
|
||||||
|
name="Stop nginx to free port 80 for acmetool",
|
||||||
|
service="nginx.service",
|
||||||
|
running=False,
|
||||||
|
enabled=True,
|
||||||
|
)
|
||||||
|
|
||||||
systemd.service(
|
systemd.service(
|
||||||
name="Setup acmetool-redirector service",
|
name="Setup acmetool-redirector service",
|
||||||
service="acmetool-redirector.service",
|
service="acmetool-redirector.service",
|
||||||
@@ -135,6 +142,15 @@ class AcmetoolDeployer(Deployer):
|
|||||||
)
|
)
|
||||||
self.need_restart_reconcile_timer = False
|
self.need_restart_reconcile_timer = False
|
||||||
|
|
||||||
|
# Add the first domain to /etc/hosts to help acmetool's self-test
|
||||||
|
# bypass external firewalls or split-brain DNS issues.
|
||||||
|
server.shell(
|
||||||
|
name=f"Add {self.domains[0]} to /etc/hosts for self-test",
|
||||||
|
commands=[
|
||||||
|
f"grep -q ' {self.domains[0]}$' /etc/hosts || echo '127.0.0.1 {self.domains[0]}' >> /etc/hosts"
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
server.shell(
|
server.shell(
|
||||||
name=f"Reconcile certificates for: {', '.join(self.domains)}",
|
name=f"Reconcile certificates for: {', '.join(self.domains)}",
|
||||||
commands=["acmetool --batch --xlog.severity=debug reconcile"],
|
commands=["acmetool --batch --xlog.severity=debug reconcile"],
|
||||||
|
|||||||
@@ -80,14 +80,22 @@ def _install_remote_venv_with_chatmaild() -> None:
|
|||||||
remote_dist_file = f"{remote_base_dir}/dist/{dist_file.name}"
|
remote_dist_file = f"{remote_base_dir}/dist/{dist_file.name}"
|
||||||
remote_venv_dir = f"{remote_base_dir}/venv"
|
remote_venv_dir = f"{remote_base_dir}/venv"
|
||||||
root_owned = dict(user="root", group="root", mode="644")
|
root_owned = dict(user="root", group="root", mode="644")
|
||||||
|
root_owned_dir = dict(user="root", group="root", mode="755")
|
||||||
|
uv_prefix = "UV_PYTHON_INSTALL_DIR=/usr/local/share/uv/python"
|
||||||
|
|
||||||
server.shell(
|
server.shell(
|
||||||
name="Install uv",
|
name="Install uv globally if not present",
|
||||||
commands=[
|
commands=[
|
||||||
"curl -LsSf https://astral.sh/uv/install.sh | INSTALLER_NO_MODIFY_PATH=1 sh",
|
"command -v uv >/dev/null 2>&1 || (curl -LsSf https://astral.sh/uv/install.sh | INSTALLER_NO_MODIFY_PATH=1 sudo sh -s -- --install-dir /usr/local/bin)",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
files.directory(
|
||||||
|
name="Ensure shared uv python directory exists",
|
||||||
|
path="/usr/local/share/uv/python",
|
||||||
|
**root_owned_dir,
|
||||||
|
)
|
||||||
|
|
||||||
files.put(
|
files.put(
|
||||||
name="Upload chatmaild source package",
|
name="Upload chatmaild source package",
|
||||||
src=dist_file.open("rb"),
|
src=dist_file.open("rb"),
|
||||||
@@ -95,10 +103,30 @@ def _install_remote_venv_with_chatmaild() -> None:
|
|||||||
create_remote_dir=True,
|
create_remote_dir=True,
|
||||||
**root_owned,
|
**root_owned,
|
||||||
)
|
)
|
||||||
|
# Ensure parent directory is accessible
|
||||||
|
files.directory(
|
||||||
|
name=f"Ensure {remote_base_dir} is accessible",
|
||||||
|
path=remote_base_dir,
|
||||||
|
**root_owned_dir,
|
||||||
|
)
|
||||||
|
files.directory(
|
||||||
|
name=f"Ensure {remote_base_dir}/dist is accessible",
|
||||||
|
path=f"{remote_base_dir}/dist",
|
||||||
|
**root_owned_dir,
|
||||||
|
)
|
||||||
|
|
||||||
server.shell(
|
server.shell(
|
||||||
name=f"chatmaild virtualenv {remote_venv_dir}",
|
name=f"chatmaild virtualenv {remote_venv_dir}",
|
||||||
commands=[f"/root/.local/bin/uv venv {remote_venv_dir} --allow-existing"],
|
commands=[f"{uv_prefix} uv venv {remote_venv_dir} --python 3.11 --allow-existing"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Ensure venv and managed pythons are accessible by other users (like www-data)
|
||||||
|
server.shell(
|
||||||
|
name="Make venv and managed pythons accessible",
|
||||||
|
commands=[
|
||||||
|
f"chmod -R a+rX {remote_venv_dir}",
|
||||||
|
"chmod -R a+rX /usr/local/share/uv/python || true",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
apt.packages(
|
apt.packages(
|
||||||
@@ -109,7 +137,7 @@ def _install_remote_venv_with_chatmaild() -> None:
|
|||||||
server.shell(
|
server.shell(
|
||||||
name=f"forced uv-pip-install {dist_file.name}",
|
name=f"forced uv-pip-install {dist_file.name}",
|
||||||
commands=[
|
commands=[
|
||||||
f"/root/.local/bin/uv pip install --python {remote_venv_dir}/bin/python --force-reinstall {remote_dist_file}"
|
f"{uv_prefix} uv pip install --python {remote_venv_dir}/bin/python --force-reinstall {remote_dist_file}"
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -448,6 +476,10 @@ class ChatmailDeployer(Deployer):
|
|||||||
self.mail_domain = mail_domain
|
self.mail_domain = mail_domain
|
||||||
|
|
||||||
def install(self):
|
def install(self):
|
||||||
|
server.shell(
|
||||||
|
name="Fix broken packages",
|
||||||
|
commands=["apt-get install -f -y"],
|
||||||
|
)
|
||||||
apt.update(name="apt update", cache_time=24 * 3600)
|
apt.update(name="apt update", cache_time=24 * 3600)
|
||||||
apt.upgrade(name="upgrade apt packages", auto_remove=True)
|
apt.upgrade(name="upgrade apt packages", auto_remove=True)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from chatmaild.config import Config
|
from chatmaild.config import Config
|
||||||
from pyinfra import host
|
from pyinfra import host
|
||||||
from pyinfra.facts.server import Arch, Sysctl
|
from pyinfra.facts.server import Arch, LinuxDistribution, Sysctl
|
||||||
from pyinfra.facts.systemd import SystemdEnabled
|
from pyinfra.facts.systemd import SystemdEnabled
|
||||||
from pyinfra.operations import apt, files, server, systemd
|
from pyinfra.operations import apt, files, server, systemd
|
||||||
|
|
||||||
@@ -50,6 +50,33 @@ class DovecotDeployer(Deployer):
|
|||||||
|
|
||||||
|
|
||||||
def _install_dovecot_package(package: str, arch: str):
|
def _install_dovecot_package(package: str, arch: str):
|
||||||
|
distro = host.get_fact(LinuxDistribution)
|
||||||
|
is_old_debian = False
|
||||||
|
if distro:
|
||||||
|
distro_id = str(distro.get("id", "")).lower()
|
||||||
|
distro_name = str(distro.get("name", "")).lower()
|
||||||
|
major_version = str(distro.get("major_version", "0"))
|
||||||
|
|
||||||
|
if "debian" in distro_id or "debian" in distro_name:
|
||||||
|
try:
|
||||||
|
# Handle cases where major_version might be '11.13'
|
||||||
|
m_ver = major_version.split(".")[0]
|
||||||
|
if m_ver.isdigit() and 0 < int(m_ver) < 12:
|
||||||
|
is_old_debian = True
|
||||||
|
except (ValueError, TypeError, IndexError):
|
||||||
|
pass
|
||||||
|
# Fallback for systems where major_version might not be parsed correctly but name contains 'bullseye'
|
||||||
|
if "bullseye" in distro_name or "buster" in distro_name:
|
||||||
|
is_old_debian = True
|
||||||
|
|
||||||
|
if is_old_debian:
|
||||||
|
apt.packages(
|
||||||
|
name=f"Install system dovecot-{package}",
|
||||||
|
packages=[f"dovecot-{package}"],
|
||||||
|
update=True,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
arch = "amd64" if arch == "x86_64" else arch
|
arch = "amd64" if arch == "x86_64" else arch
|
||||||
arch = "arm64" if arch == "aarch64" else arch
|
arch = "arm64" if arch == "aarch64" else arch
|
||||||
url = f"https://download.delta.chat/dovecot/dovecot-{package}_2.3.21%2Bdfsg1-3_{arch}.deb"
|
url = f"https://download.delta.chat/dovecot/dovecot-{package}_2.3.21%2Bdfsg1-3_{arch}.deb"
|
||||||
@@ -80,7 +107,18 @@ def _install_dovecot_package(package: str, arch: str):
|
|||||||
cache_time=60 * 60 * 24 * 365 * 10, # never redownload the package
|
cache_time=60 * 60 * 24 * 365 * 10, # never redownload the package
|
||||||
)
|
)
|
||||||
|
|
||||||
apt.deb(name=f"Install dovecot-{package}", src=deb_filename)
|
# Use a shell command to try installing the custom .deb and fallback if it fails.
|
||||||
|
# This prevents the whole deployment from failing if custom dovecot is not compatible.
|
||||||
|
server.shell(
|
||||||
|
name=f"Install dovecot-{package} (custom or system fallback)",
|
||||||
|
commands=[
|
||||||
|
f"if ! dpkg -i {deb_filename}; then "
|
||||||
|
f"echo 'HEY: dovecot the custom is not installed, we install your os main one' >&2; "
|
||||||
|
f"DEBIAN_FRONTEND=noninteractive apt-get install -f -y; "
|
||||||
|
f"DEBIAN_FRONTEND=noninteractive apt-get install -y dovecot-{package}; "
|
||||||
|
f"fi"
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _configure_dovecot(config: Config, debug: bool = False) -> (bool, bool):
|
def _configure_dovecot(config: Config, debug: bool = False) -> (bool, bool):
|
||||||
|
|||||||
@@ -229,7 +229,7 @@ ssl = required
|
|||||||
ssl_cert = </var/lib/acme/live/{{ config.mail_domain }}/fullchain
|
ssl_cert = </var/lib/acme/live/{{ config.mail_domain }}/fullchain
|
||||||
ssl_key = </var/lib/acme/live/{{ config.mail_domain }}/privkey
|
ssl_key = </var/lib/acme/live/{{ config.mail_domain }}/privkey
|
||||||
ssl_dh = </usr/share/dovecot/dh.pem
|
ssl_dh = </usr/share/dovecot/dh.pem
|
||||||
ssl_min_protocol = TLSv1.3
|
ssl_min_protocol = TLSv1.2
|
||||||
ssl_prefer_server_ciphers = yes
|
ssl_prefer_server_ciphers = yes
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from chatmaild.config import Config
|
from chatmaild.config import Config
|
||||||
from pyinfra.operations import apt, files, systemd
|
from pyinfra.operations import apt, files, server, systemd
|
||||||
|
|
||||||
from cmdeploy.basedeploy import (
|
from cmdeploy.basedeploy import (
|
||||||
Deployer,
|
Deployer,
|
||||||
@@ -43,6 +43,13 @@ class NginxDeployer(Deployer):
|
|||||||
name="Install nginx",
|
name="Install nginx",
|
||||||
packages=["nginx", "libnginx-mod-stream"],
|
packages=["nginx", "libnginx-mod-stream"],
|
||||||
)
|
)
|
||||||
|
server.shell(
|
||||||
|
name="Remove default nginx configuration",
|
||||||
|
commands=[
|
||||||
|
"rm -f /etc/nginx/sites-enabled/default",
|
||||||
|
"rm -f /etc/nginx/conf.d/default.conf",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
files.file("/usr/sbin/policy-rc.d", present=False)
|
files.file("/usr/sbin/policy-rc.d", present=False)
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ smtp_tls_security_level=verify
|
|||||||
smtp_tls_servername = hostname
|
smtp_tls_servername = hostname
|
||||||
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
|
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
|
||||||
smtp_tls_policy_maps = inline:{nauta.cu=may}
|
smtp_tls_policy_maps = inline:{nauta.cu=may}
|
||||||
smtp_tls_protocols = >=TLSv1.2
|
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
|
||||||
smtp_tls_mandatory_protocols = >=TLSv1.2
|
smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
|
||||||
|
|
||||||
# Disable anonymous cipher suites
|
# Disable anonymous cipher suites
|
||||||
# and known insecure algorithms.
|
# and known insecure algorithms.
|
||||||
|
|||||||
@@ -10,17 +10,15 @@
|
|||||||
# (yes) (yes) (no) (never) (100)
|
# (yes) (yes) (no) (never) (100)
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
{% if debug == true %}
|
{% if debug == true %}
|
||||||
smtp inet n - y - - smtpd -v
|
smtp inet n - n - - smtpd
|
||||||
{%- else %}
|
|
||||||
smtp inet n - y - - smtpd
|
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
-o smtpd_tls_security_level=encrypt
|
-o smtpd_tls_security_level=encrypt
|
||||||
-o smtpd_tls_mandatory_protocols=>=TLSv1.2
|
-o smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3,!TLSv1,!TLSv1.1
|
||||||
-o smtpd_proxy_filter=127.0.0.1:{{ config.filtermail_smtp_port_incoming }}
|
-o smtpd_proxy_filter=127.0.0.1:{{ config.filtermail_smtp_port_incoming }}
|
||||||
submission inet n - y - 5000 smtpd
|
submission inet n - n - 5000 smtpd
|
||||||
-o syslog_name=postfix/submission
|
-o syslog_name=postfix/submission
|
||||||
-o smtpd_tls_security_level=encrypt
|
-o smtpd_tls_security_level=encrypt
|
||||||
-o smtpd_tls_mandatory_protocols=>=TLSv1.3
|
-o smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3,!TLSv1,!TLSv1.1
|
||||||
-o smtpd_sasl_auth_enable=yes
|
-o smtpd_sasl_auth_enable=yes
|
||||||
-o smtpd_sasl_type=dovecot
|
-o smtpd_sasl_type=dovecot
|
||||||
-o smtpd_sasl_path=private/auth
|
-o smtpd_sasl_path=private/auth
|
||||||
@@ -33,11 +31,11 @@ submission inet n - y - 5000 smtpd
|
|||||||
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
|
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
|
||||||
-o smtpd_client_connection_count_limit=1000
|
-o smtpd_client_connection_count_limit=1000
|
||||||
-o smtpd_proxy_filter=127.0.0.1:{{ config.filtermail_smtp_port }}
|
-o smtpd_proxy_filter=127.0.0.1:{{ config.filtermail_smtp_port }}
|
||||||
smtps inet n - y - 5000 smtpd
|
smtps inet n - n - 5000 smtpd
|
||||||
-o syslog_name=postfix/smtps
|
-o syslog_name=postfix/smtps
|
||||||
-o smtpd_tls_wrappermode=yes
|
-o smtpd_tls_wrappermode=yes
|
||||||
-o smtpd_tls_security_level=encrypt
|
-o smtpd_tls_security_level=encrypt
|
||||||
-o smtpd_tls_mandatory_protocols=>=TLSv1.3
|
-o smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3,!TLSv1,!TLSv1.1
|
||||||
-o smtpd_sasl_auth_enable=yes
|
-o smtpd_sasl_auth_enable=yes
|
||||||
-o smtpd_sasl_type=dovecot
|
-o smtpd_sasl_type=dovecot
|
||||||
-o smtpd_sasl_path=private/auth
|
-o smtpd_sasl_path=private/auth
|
||||||
|
|||||||
7
init.sh
7
init.sh
@@ -8,7 +8,7 @@ sudo apt install -y git curl wget python3-dev gcc python3 nano sed
|
|||||||
|
|
||||||
# 1.1 Install uv
|
# 1.1 Install uv
|
||||||
export PATH="$HOME/.local/bin:/root/.local/bin:$PATH"
|
export PATH="$HOME/.local/bin:/root/.local/bin:$PATH"
|
||||||
if ! command -v uv &> /dev/null; then
|
if ! command -v uv > /dev/null 2>&1; then
|
||||||
if [ -f "/root/.local/bin/uv" ]; then
|
if [ -f "/root/.local/bin/uv" ]; then
|
||||||
export PATH="/root/.local/bin:$PATH"
|
export PATH="/root/.local/bin:$PATH"
|
||||||
elif [ -f "$HOME/.local/bin/uv" ]; then
|
elif [ -f "$HOME/.local/bin/uv" ]; then
|
||||||
@@ -16,7 +16,7 @@ if ! command -v uv &> /dev/null; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! command -v uv &> /dev/null; then
|
if ! command -v uv > /dev/null 2>&1; then
|
||||||
echo "--- Installing uv ---"
|
echo "--- Installing uv ---"
|
||||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||||
# Ensure uv is in PATH for the current script
|
# Ensure uv is in PATH for the current script
|
||||||
@@ -43,7 +43,8 @@ read -p "Enter your email for ACME/Let's Encrypt: " ACME_EMAIL
|
|||||||
|
|
||||||
# 5. Initialize configuration
|
# 5. Initialize configuration
|
||||||
echo "--- Initializing chatmail configuration ---"
|
echo "--- Initializing chatmail configuration ---"
|
||||||
./scripts/cmdeploy init "$MAIL_DOMAIN"
|
./scripts/cmdeploy init "$MAIL_DOMAIN" || true
|
||||||
|
|
||||||
|
|
||||||
# 6. Modify chatmail.ini with specific requirements
|
# 6. Modify chatmail.ini with specific requirements
|
||||||
echo "--- Customizing chatmail.ini ---"
|
echo "--- Customizing chatmail.ini ---"
|
||||||
|
|||||||
29
pyproject.toml
Normal file
29
pyproject.toml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
[project]
|
||||||
|
name = "relay-ir"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Chatmail relay workspace"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.11"
|
||||||
|
dependencies = [
|
||||||
|
"chatmaild",
|
||||||
|
"cmdeploy",
|
||||||
|
"sphinx",
|
||||||
|
"sphinxcontrib-mermaid",
|
||||||
|
"sphinx-autobuild",
|
||||||
|
"furo",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.uv]
|
||||||
|
managed = true
|
||||||
|
package = false
|
||||||
|
|
||||||
|
[tool.uv.workspace]
|
||||||
|
members = ["chatmaild", "cmdeploy"]
|
||||||
|
|
||||||
|
[tool.uv.sources]
|
||||||
|
chatmaild = { workspace = true }
|
||||||
|
cmdeploy = { workspace = true }
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=61"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
@@ -21,7 +21,7 @@ fi
|
|||||||
# Ensure uv is in PATH
|
# Ensure uv is in PATH
|
||||||
export PATH="$HOME/.local/bin:/root/.local/bin:$PATH"
|
export PATH="$HOME/.local/bin:/root/.local/bin:$PATH"
|
||||||
|
|
||||||
if ! command -v uv &> /dev/null; then
|
if ! command -v uv > /dev/null 2>&1; then
|
||||||
if [ -f "/root/.local/bin/uv" ]; then
|
if [ -f "/root/.local/bin/uv" ]; then
|
||||||
export PATH="/root/.local/bin:$PATH"
|
export PATH="/root/.local/bin:$PATH"
|
||||||
elif [ -f "$HOME/.local/bin/uv" ]; then
|
elif [ -f "$HOME/.local/bin/uv" ]; then
|
||||||
@@ -29,13 +29,9 @@ if ! command -v uv &> /dev/null; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! command -v uv &> /dev/null; then
|
if ! command -v uv > /dev/null 2>&1; then
|
||||||
echo "uv not found. Please install it first or run init.sh"
|
echo "uv not found. Please install it first or run init.sh"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
uv venv venv
|
uv sync --python 3.11
|
||||||
|
|
||||||
uv pip install -e chatmaild
|
|
||||||
uv pip install -e cmdeploy
|
|
||||||
uv pip install sphinx sphinxcontrib-mermaid sphinx-autobuild furo # for building the docs
|
|
||||||
|
|||||||
Reference in New Issue
Block a user