It prints all destinations with the number of recipients
and all the reasons. Operator can then try
to fix the problems for destinations,
e.g. by manually adding reverse proxy
addresses to /etc/hosts for failing domains
or routing IP addresses to another interface.
replaces #870fix#851
* fix(dns): address possible IndexError
* fix(dns): remove redundant docstring
* fix(dns): don't make NS explicit if None
* bump cmlxc to 0.13.5 which fixes a powerdns config issue
* remove the unneccessary SOA mocks, simplify mock tests, and run ruff format
Co-authored-by: holger krekel <holger@merlinux.eu>
Commit 825831e purges resolvconf, however the unbound service
activates a 'wants' unit for async resolvconf updates. This
results in errors in systemd startup as the unit will now always fail.
Stop and disable the unbound-resolvconf unit activation
Correct the systemd unit modifications in 98bc1503 that lead to startup
failures in some instances. Switch to After+Wants = network-online.target
and add RestartSec=2s to give late-binding more interfaces time to appear.
In the deployer, capture the files.template() return value and
appropriately set need_restart and daemon_reload.
For compatibility with madmail,
we want to use path with no leading
slash. This change saves us from
having to follow redirects.
Signed-off-by: Jagoda Ślązak <jslazak@jslazak.com>
On push to main send a repository_dispatch event to chatmail/docker with
relay_ref, relay_sha, and relay_sha_short.
This triggers docker-ci.yaml to build a new Docker image from
the updated relay code, push to GHCR, and eventually run integration
tests via cmlxc's reusable lxc-test workflow.
Requires DOCKER_DISPATCH_TOKEN secret with repo scope on
chatmail/docker.
Also set workflow_dispatch to allow manual triggering of Docker builds
from any relay branch via the GitHub UI.
This change reverts 06560dd071
Main reason for using the same address for sending
as the one used in DNS is to pass FCrDNS
(forward-confirmed reverse DNS) checks:
IP address used by SMTP client should resolve
to the domain which in turn resolves to the same IP.
chatmail relays don't do check reverse DNS
for incoming connections,
but other email servers may do and reject email
if the check does not pass.
Most chatmail relays only have one IP address per address family,
so this configuration does not change anything.
For chatmail relays that have multiple addresses
and only publishing one IP to DNS,
source address used for outgoing SMTP connections
should be the public IP.
This can be ensured by configuring the source
address in the routing table,
e.g. with the `src` argument
to `ip route add/change/replace` command.
Solving this by binding SMTP client address
on the application level prevents chatmail relays
from configuring alternative routes.
Besides, some chatmail relays are NATed
and NAT is responsible for translating the address to the public one,
in which case using `smtp_bind_address_enforce`
will result in unnecessarily deferring all mails.
doc/README.md was outdated, it did not include sphinxcontrib-mermaid.
Better use scripts/initenv.sh which already installs all dependencies
and is used in CI.
Both dovecot-quota-threshold triggers and the daily expiry routine
will now expunge oldest messages from mailboxes automatically
when the mailbox reaches 75% of max_mailbox_size.
Delta Chat users should not see any warnings (at 80/95 percent) or bounce messages,
and existing over-quota mailboxes should start receiving mails again.
1e376f7 explicitly installed resolvconf to fix DNS breakage after
ff541b8 disabled APT recommends. But resolvconf adds dynamic resolver
management and is unnecessary on a server. Similarly, systemd-resolved
competes for /etc/resolv.conf.
Write a static resolv.conf with unbound as primary and 9.9.9.9 as
fallback. Purge resolvconf, stop and mask systemd-resolved to prevent it
from overriding the static configuration.
This is aimed at reducing SSD wear level.
SSDs wear out because of writes
according to <https://superuser.com/a/440219/1777696>,
so anything reducing the writes should be helpful.
For online users Maildir format that we use
results in first storing the message in new/
and then moving to cur/ and then maybe even deleting
it immediately for users with a single device
or bots. Syncing all these changes to disk
is unnecessary and wears SSDs.
Since ff541b8 introduced APT::Install-Recommends "false", we need to
explicitly install resolvconf. Fixes DNS breakage caused by apt.upgrade
with auto_remove=True purging resolvconf as an orphan and removing
'nameserver 127.0.0.1' in /etc/resolv.conf that pointed to the local
unbound, in consequence DNS resolution breaks and filtermail-incoming
exits because it cannot find resolvers.
When a previous deploy installed dovecot packages but the restart was
blocked (policy-rc.d) or the deploy aborted before activate(), the next
deploy sees the correct package version already installed and skips
restart. Extend activate() to check /proc/MainPID/exe for "(deleted)"
before the restart decision.
Since http port will be used for MTA-to-MTA,
it should be suffixed with "incoming" for consistency.
This will also make it clearer if we decide to
introduce client-relay http channel in the future.
Signed-off-by: Jagoda Ślązak <jslazak@jslazak.com>
Restart dovecot after package replacement even when `policy-rc.d` blocks
package-triggered restarts, avoid reinstalling already-correct packages.
Adds proper version separation for dovecot packages:
- Split DOVECOT_VERSION into DOVECOT_ARCHIVE_VERSION (for URLs/filenames)
and DOVECOT_PACKAGE_VERSION (epoch-prefixed for dpkg matching).
- Update _download_dovecot_package() to return (path, changed) tuple
so install() can track whether packages triggered restart intent.
- Use self.need_restart |= changed consistently throughout deployer.
- Move self.need_restart = True inside `if debs:` block -- previously
the apt pin file write unconditionally forced a restart every deploy.
- Comment on dpkg retry pattern (first dpkg may fail on missing deps,
apt-get --fix-broken resolves, then dpkg retries).
Authored-by: Alex V. <119082209+Retengart@users.noreply.github.com>
fixup
Ensure the preferences.d snippet that pins dovecot packages to block
Debian dist-upgrades is owned by root:root and has 644 permissions.
Files in this directory are generally expected to be world readable to ensure unprivileged operations such as apt-get in simulation mode. Having them not world readable breaks such usages.
As our .deb packages use Debian's version naming scheme, deploy an apt
preferences file that sets Pin-Priority: -1 for all dovecot-* packages
for every version of dovecot-* from every origin.
This change was accidentally added in cf96be2cbb
Relay should not stop validating TLS certificates of other relays
just because it has a self-signed or externally managed certificate.
Externally managed certificate is likely to even be valid.
Since change 635ac7 we try to install Dovecot, even if it is already
running, which fails Dovecot upgrades fail when the installed version
differs from the target because dovecot-imapd/lmtpd dependencies
on dovecot-core: packages are installed one at a time via apt.deb(),
i.e. `dpkg -i`, and dpkg cannot satisfy them dependencies:
```
dpkg: dependency problems prevent configuration of dovecot-imapd:
dovecot-imapd depends on dovecot-core (= 1:2.3.21+dfsg1-3); however:
Version of dovecot-core on system is 1:2.3.21.1+dfsg1-1~bpo12+1.
```
Split _install_dovecot_package into _download_dovecot_package (download
only, return path) and a single server.shell call that passes all .deb
files to dpkg -i together. Uses the same 3-step pattern as pyinfra's
apt.deb: tolerant first dpkg -i, apt-get --fix-broken, then final
dpkg -i to fail if there are still errors.
Prevent services from auto-starting during package installation by
installing a policy-rc.d that exits 101. This avoids dovecot startup
failures when no TLS cert exists yet (e.g. acmetool failed on first run).
Picked out of 62fe113b from hpk/lxcdeploy branch.
The old code did not install updates when the service was running; check
installed version instead of systemd status. Also, rewrite install logic
to extract dovecot version and hashes as module-level constants.
Use blocked_service_startup from lxcdeploy branch as it solves our
problem here too.
Disables IP verification by upgrading filtermail to v0.6,
changelog: <https://github.com/chatmail/filtermail/releases/tag/v0.6.0>
Messages using domain-literal addresses no longer require
to match the origin SMTP connection IP anymore.
This allows for example a relay using IPv4 email addresses
to send messages to other relays over IPv6.
This is not considering a breaking change as IP-address-only
relays are not considered a stable feature.
Signed-off-by: Jagoda Ślązak <jslazak@jslazak.com>
ConnectionRefusedError/FileNotFoundError/TimeoutError from
turn_credentials() would kill the dict proxy connection.
Return N (not found) response instead and log the error.
- Reject localparts with chars outside [a-z0-9._-] to prevent
filesystem issues from crafted usernames via IMAP/SMTP auth
- Use filelock to serialize concurrent account creation for same
address, preventing TOCTOU race where two threads both create
an account and last writer wins
Per Python docs, secrets module should be used for security-sensitive
data. random.choices uses Mersenne Twister PRNG which is predictable.
secrets.choice was already used for password generation in the same file.
Similar data is already generated by fsreport
available for the relay operator
and metrics for prometheus are generated by mtail.
Closes <https://github.com/chatmail/relay/issues/431>
Upgrade to filtermail v0.5, which has a built-in DKIM verifier
and disable OpenDKIM on reinject_incoming.
Signed-off-by: Jagoda Ślązak <jslazak@jslazak.com>
Adds a new tls_external_cert_and_key config option for chatmail servers
that manage their own TLS certificates (e.g. via an external ACME client
or a load balancer).
A systemd path unit (tls-cert-reload.path) watches the certificate file
via inotify and automatically reloads dovecot and nginx when it changes.
Postfix reads certs per TLS handshake so needs no reload.
Also extracts openssl_selfsigned_args() so cert generation parameters
are shared between SelfSignedTlsDeployer and the e2e test.
* cleanup: remove CFFI deltachat bindings usage, and consolidate test support with rpc-bindings
major simplification: all chatmail fixtures used in the test are now created inside the cmdeploy plugin,
and do not inherit anything from other fixture machineries, let alone the legacy deltachat CFFI ones.
also fix that pytest report headers show correct chatmail domains under test
- chatmaild:
- basedeploy.py: Add has_systemd() guard. During Docker image builds
there's no running systemd, so deployers that query SystemdEnabled
facts would crash; this change might also be helpful for non-systemd
platforms.
- cmdeploy:
- cmdeploy.py:
- when deploying to @docker, auto-set CHATMAIL_NOPORTCHECK and
CHATMAIL_NOSYSCTL since neither makes sense inside a container
- --config default now reads CHATMAIL_INI env var, so Docker
entrypoints can point to a mounted ini without CLI flags.
- deployers.py:
- skip port check / CHATMAIL_NOPORTCHECK
- skip echobot systemd cleanup w/ has_systemd
- dovecot/deployer.py:
- Guard sysctl writes behind CHATMAIL_NOSYSCTL
- invert dovecot install check so it works without systemd
- sshexec.py: Add __call__ to LocalExec so cmdeploy status works with
@local target. Without it, cmdeploy status tried to call the
executor directly and got TypeError.
Consolidated from j4n/docker branch commits (selection):
- 8953fde feat(cmdeploy): read CHATMAIL_INI env var for default --config path
- 81d7782 fix(cmdeploy): add __call__ to LocalExec so status works with @local
- 8bba78e docker: disable port check if docker is running. fix#694
- 865b514 docker: replace config flags with env vars, drop docker param (instead of f26cb08)
Files: cmdeploy/src/cmdeploy/{basedeploy,cmdeploy,deployers,sshexec,dovecot/deployer}.py
Co-authored-by: Keonik1 <keonik.dev@gmail.com>
Co-authored-by: missytake <missytake@systemli.org>
This elimitates the problem of acmetool failing
to start when nginx is installed already and uses port 80.
This also makes nginx redirect HTTP requests to HTTPS
for setups that don't have acmetool.
feat: support self-signed TLS via underscore domain convention
Domains starting with "_" (e.g. _chat.example.org) automatically use
self-signed TLS certificates instead of ACME/Let's Encrypt. The TLS
mode is derived from the domain name — no separate config option needed.
Internally, when config.tls_cert_mode is "self" (underscore domain):
- Generate self-signed certificates via openssl
- Set Postfix smtp_tls_security_level to "encrypt" (opportunistic TLS)
- Add smtp_tls_policy_map entry for underscore domains
- Skip ACME, MTA-STS and www CNAME checks in `cmdeploy dns`
- Serve /new via GET (not redirect to dcaccount:) with rate-limiting
(nginx limit_req, 2r/s burst=5)
- Return dclogin: URLs with ic=3 (AcceptInvalidCertificates) from /new
- Render QR codes client-side via JavaScript and qrcode-svg
- Use config.tls_cert_path/tls_key_path in Postfix, Dovecot and nginx
templates instead of hardcoded ACME paths
Ensure that the interface for mtail_address is available and fix a bug
in port checking where single services were always passing regardless of
the specified service name.
filtermail rate limiter is using leaky bucket
algorithm (GCRA).
Exceeting the limit requires sending
at least max_user_send_per_minute
messages to exhaust allowed burst,
and then sending messages faster
than the leak rate.
As we don't know how fast is the network
between the server and test runner,
try to send 3 times max_user_send_per_minute
messages to ensure the test does not
fail randomly.
The ! character in != is an invalid token in Dovecot's unified filter
language (2.3.12+). The parser expected a comparison operator (=, >, <)
and choked on !.
This adds exporting of some dovecot event metrics to help debugging slow IMAP login and hibernation. For now, re-using mtail_address config flag and configure the port of the dovecot exporter to be 3904.
Currently we strip the DKIM-Signature header in the OpenDKIM final.lua
script after validation of the signature. We sign all messages upon
submission, but we do not verify messages which are from a local account
and delivered to another local account.
This corrects the problem and ensures that the plaintext headers of a
local to local delivery are sanitized the same as a message received
from another server.
The functionality in final.lua to strip the DKIM-Signature header can
now be retired.
* docs: update index reference
* docs: adds control machine migration instructions
* docs: rename index ref
* docs: remove maddy-chatmail (404)
* docs: consistent underlining in header text
* docs: remove dedicated page reference
* docs: remove dedicated page for control machine migration
* docs: condense deployment machine migration into getting started per feedback
* docs: correct link to madmail
* docs: update verbiage based on feedback
Deployments to test servers will not be cancelled anymore,
but it is not clear if we even want it.
This setup is much simpler because it only depends
on GitHub Actions features and does not allocate
a runner just to sleep there and wait in the queue.
* docs: update migration guide after nine migration
* use $OLD_IP4 and $NEW_IP4 to make docs more readable. Also streamline "set TTL to 5 minute" phrasing a bit.
* fix tar commands
* refactor: streamline and refactor the migration guide to provide more clarity and focus
* recommend a "higher TTL" concrete value
Co-authored-by: missytake <missytake@systemli.org>
* scriptify another location
---------
Co-authored-by: missytake <missytake@systemli.org>
On FreeBSD 127.0.0.2 is not assigned to any interface by default,
so 127.0.0.2 source address hack cannot be used to make OpenDKIM
verify the signature instead of signing.
This change sets InternalHosts to `-` so no IP addresses
make OpenDKIM sign the message. Instead of IP address,
OpenDKIM in the outgoing pipeline is explicitly told
to sign messages by setting `{daemon_name}` macro to `ORIGINATING`.
I am running git-cliff 2.11.0.
Ran `git-cliff --init` to generate `cliff.toml`.
Removed emojis, replaced `doc` with `docs` to match chatmail core
convention.
The original https://github.com/chatmail/relay/pull/533 attempted to remove the header through postfix, but that is too early. Instead, remove the headers in the OpenDKIM `final.lua` script after the validation.
smtp_tls_mandatory_protocols does not affect port 25
because we require STARTTLS on port 25 since commit
8d7e1dad0e
We don't have any smtpd ports with opportunistic TLS.
Submission ports require TLSv1.3 and starting with this commit
MX port will require TLSv1.2 instead of TLSv1.
I have not managed to connect using TLSv1.1
even without this fix to reproduce the problem,
but I have checked that setting
`-o smtpd_tls_mandatory_protocols=>=TLSv1.3`
does not allow to connect using TLSv1.2 anymore using
`openssl s_client -connect example.org:25 -starttls smtp -tls1_2`.
`smtpd_tls_protocols` setting is removed
because it does not affect anything except the internal ports
and its `git blame` points to the wrong commit.
According to
<https://www.postfix.org/postconf.5.html#smtp_tls_security_level>
for outgoing connections with smtp_tls_security_level
`encrypt` and higher (such as `verify` that we currently use)
the setting `smtp_tls_mandatory_protocols`
is used instead of `smtp_tls_protocols`.
According to `postconf -d`
(and `postconf` because the default is not changed)
current setting value is `smtp_tls_mandatory_protocols = >=TLSv1`.
But we only want to connect outside with TLS 1.2 and TLS 1.3.
`smtp_tls_protocols` which was already set to `>= TLSv1.2`
in commit 0155f32df6
only affected outgoing connections with the `may` level
exception set for nauta.cu domain via `smtp_tls_policy_maps`
which does not support STARTTLS at all.
* refactor: Move all imports to top of cmdeploy/__init__.py
* refactor: Move addition of 9.9.9.9 resolver earlier
- Moved the "Add 9.9.9.9 to resolv.conf" step earlier, before the
creation of users or updates to any config files. This should not
affect any of those operations. Moving this step earlier makes it
easier to accommodate the restructuring of the deployment process
into separate components with separate stages for install,
configure, and activate.
- Added a Deployer class that defines the base for objects that will
handle installation of individual components, with install,
configure, and activate stages.
- The CMDEPLOY_STAGES environment variable is used to determine what
stages to run. If this is not defined, all stages run as usual.
- Added import of Deployer to cmdeploy/__init__.py. This is not yet
used, but the next series of commits will use it.
- In deploy_chatmail(), define an empty list of deployers, and call
the create_groups() and create_users() methods for the items in the
list. This list will get filled with Deployer objects in the next
series of commits.
* refactor: Add DovecotDeployer
* refactor: Add PostfixDeployer
- Removed now-unused 'debug' variable from deploy_chatmail().
* refactor: Add NginxDeployer
- Use policy-rc.d during nginx install. This is needed to keep nginx
from starting up and interfering with acmetool. For more information see:
- https://serverfault.com/questions/861583/how-to-stop-nginx-from-being-automatically-started-on-install
- https://major.io/p/install-debian-packages-without-starting-daemons/
- https://people.debian.org/~hmh/invokerc.d-policyrc.d-specification.txt
* refactor: Add OpendkimDeployer
- Note that this moves the installation of the opendkim package
earlier in the deployment sequence. Previously, it was installed
during the _configure_opendkim() routine.
* refactor: Add UnboundDeployer
* refactor: Add IrohDeployer
- This splits the existing deploy_iroh_relay() routine into methods
for the install, configure, and activate stages.
* refactor: Add JournaldDeployer
* refactor: Add AcmetoolDeployer
- This splits the existing deploy_acmetool() routine into methods for
the install, configure, and activate stages.
* refactor: Add MtailDeployer
- This splits the existing deploy_mtail() routine into methods for the
install, configure, and activate stages.
* refactor: Add MtastsDeployer
- This splits the existing _uninstall_mta_sts_daemon() routine into
methods for the configure and activate stages.
* refactor: Add RspamdDeployer
- This replaces the existing _remove_rspamd() routine with a method
for the install stage.
* refactor: Split _install_remote_venv_with_chatmaild into stages
- Split _install_remote_venv_with_chatmaild() into three routines, to
handle the install, configure, and activate stages.
- This moves the upload of chatmail.ini later in the deployment
process, because it is a configuration file specific to the
instance, not software installation that would be uniform across all
deployments.
* refactor: Add ChatmailVenvDeployer
* refactor: Add ChatmailDeployer
- This moves the installation of cron earlier in the deployment sequence.
* refactor: Add FcgiwrapDeployer
* refactor: Add EchobotDeployer
- This class is a special case because it has a dependency on the
Postfix and Dovecot deployers. When deciding whether to restart the
echobot service, it needs to know whether the Postfix and Dovecot
deployers restarted their services. To support this dependency, the
PostfixDeployer and DovecotDeployer objects are passed to the
EchobotDeployer object, so it can check their was_restarted
attributes.
* refactor: Add WebsiteDeployer
- This adds a step to create /var/www in the install stage, because
the directory needs to exist for the rsync in the configure stage to
work.
* refactor: Add TurnDeployer
- This splits the existing deploy_turn_server() routine into methods
for the install, configure, and activate stages.
* refactor: Move curl installation from IrohDeployer to ChatmailDeployer
- The 'curl' program is used in TurnDeployer and IrohDeployer, so it
makes more sense to install it at the beginning in ChatmailDeployer,
rather than have each thing that uses it install it separately.
* refactor: Reorder deploy_chatmail()
- The previous commits that added Deployer classes mostly kept
deployment operations in the same order that they were in before.
To organize the process into separate stages for install, configure,
and activate, we need to reorder the method calls. This is the
commit that does that, and thus this is the commit that has the
largest effect on the order of operations.
- The calls for the deployer objects are all reordered here so that
the methods are called in the same sequence for each stage. This
will allow us to collect the calls into loops in the next commit.
This commit provides a way to see a diff showing exactly how the
sequence changed.
- The sequence of deployers was largely based on preserving the order
of the "activate" stage, as this seems like the place order might be
the most likely to matter. Installation of packages and
configuration of files should generally be able to run in any order.
(ChatmailDeployer handles updating the apt data, and therefore needs
to be first, however.)
* refactor: Call install, configure, and activate methods in loops
- Revised deploy_chatmail() to use all_deployers to call the
install(), configure(), and activate() methods on all the deployers,
rather than listing them explicitly in the code.
* docs: Add architectural information about deployer classes
- Updated overview.rst to describe the Deployer class hierarchy and
the motivations behind it.
* fix: Block unbound from starting up on install
- On an IPv4-only system, if unbound is started but not configured, it
causes subsequent steps to fail to resolve hosts.
- Revised UnboundDeployer.install_impl() to use policy-rc.d to prevent
the service from starting when installed. This is the same
mechanism used to keep nginx from starting on install.
* feat: Remove obs-home-deltachat.gpg
- We don't install Dovecot from OBS anymore.
- Removed files.put() that creates
/etc/apt/keyrings/obs-home-deltachat.gpg; replaced this with a
files.file() that sets present=False to remove the file from any
existing installations where it already has been installed.
- Removed now-unused obs-home-deltachat.gpg file.
- Clarified description of sources.list operation.
- Suggested in review by missytake and hpk42.
* feat: Reorder deployers
- Moved fcgiwrap before nginx.
- Exchanged order of turn and unbound.
- Moved journald as early as possible.
- Suggested in review by missytake.
* chore: Add CHANGELOG.md entry for cmdeploy refactor
* refactor: Move unit list to ChatmailVenvDeployer
- Split _configure_remote_venv_with_chatmaild() into two functions.
_configure_remote_venv_with_chatmaild() handles details specific to
the "venv", while the new _configure_remote_units() is a more
general function that is applicable to several services.
- Renamed _activate_remote_venv_with_chatmaild() to
_activate_remote_units() because doesn't have anything
venv-specific.
- Removed list of units from helper functions (where it appeared
twice); moved it to ChatmailVenvDeployer, where its is passed as an
argument to _configure_remote_units() and _activate_remote_units().
* refactor: Move turnserver out of ChatmailVenvDeployer
- Revised TurnDeployer to use _configure_remote_units() and
_activate_remote_units(). This class no longer uses need_restart
and daemon_reload attributes to keep track of state. The activate
stage of ChatmailVenvDeployer was unconditionally restarting the
service every time, so we don't need to keep track of extra state in
an attempt to avoid restarting it; we can just handle the
unconditional restart in TurnDeployer.activate_impl().
- Removed turnserver from the unit list in ChatmailVenvDeployer.
* refactor: Move echobot out of ChatmailVenvDeployer
- Revised EchobotDeployer to use _configure_remote_units() and
_activate_remote_units(). The 'activate' stage of
ChatmailVenvDeployer was unconditionally restarting the service
every time, so EchobotDeployer no longer needs to depend on the
was_restarted attributes of the postfix and dovecot deployers in an
attempt to avoid restarting it; we can just handle the unconditional
restart in EchobotDeployer.activate_impl().
- Removed echobot from the unit list in ChatmailVenvDeployer.
- Removed now-unused was_restarted attribute from PostfixDeployer and
DovecotDeployer.
* refactor: Move doveauth out of ChatmailVenvDeployer
- Revised DovecotDeployer to use _configure_remote_units() and
_activate_remote_units() to deploy doveauth. This keeps the
Dovecot-related services in a single deployer class, leaving only
services that are part of the chatmail project in
ChatmailVenvDeployer.
- Removed doveauth from the unit list in ChatmailVenvDeployer.
* strike unnccessary deployer variables
* remove indirection with "stages"
* simplify required_users configuration (a method is not needed for now)
* further reduce indirections for staged install
* now that Deployer class is clean and not mixed with what is in Deployment, use the simpler "install", "configure" and "activate" namings instead of *_impl
* remove static method and Make Deployer instances not set any default state
* strike unneccessary *,** argument flexibility
* use a Deployer for setting the remote git hash
* refactor: Revise AcmetoolDeployer for new Deployer interface
* style: Formatting revisions
* refactor: Pass all constructor arguments by position
- The constructor arguments do not have default values; they are all
required. Revised deploy_chatmail() to pass them by position rather
than name, so that the caller is not coupled to the names of the
arguments inside the method definition.
* refactor: Simplify interface to Deployer.install()
- In the current code, the only class using the interface that sets
need_restart() from the return value of the install() method was
IrohDeployer. That interface was created when the install method
was a static method, but now it is an instance method with access to
'self'. Therefore, we don't need to pass anything up to the caller
to have them set the attribute, we can just set it.
- Revised IrohDeployer.install() to set self.need_restart directly,
rather than returning a value.
- Revised Deployment.install() to ignore the return value of the
deployers' install() methods.
- need_restart is still present in the base Deployer class to ensure
that it is always defined, even when classes do not set it in a
constructor. Apart from this initialization for convenience, there
is no longer any specific exposure of need_restart in the interface
of the Deployer class.
- In general, install() methods should use 'self' as little as
possible, preferably not at all. In particular, install() methods
should never depend on "config" data, such as the config dictionary
in self.config or specific values like self.mail_domain. This
ensures that these methods can be used to perform generic
installation operations that are applicable across multiple relay
deployments, and therefore can be called in the process of building
a general-purpose container image.
* docs: Update cmdeploy architecture details
- Revised cmdeploy documentation in doc/source/overview.rst to reflect
the recent revisions to the Deployer interface.
* docs: Remove section about use of objects
---------
Co-authored-by: holger krekel <holger@merlinux.eu>
* cmdeploy: fix status cmd after sshexec rework
* tests: test cmdeploy status
* tests: move test to online tests
* tests: require chatmail_config for status test
refactor README.rst and architecture file into sphinx doc project, automatically deploying on main merges and PRs.
* add FAQs from https://chatmail.at/relays landing page
* fix links, and streamline postfix/dovecot mentioning
* add linkcheck to CI, fix several links and streamlihne DKIM section while at it
* some streamlining, rename to "overview"
* ci: upload documentation to chatmail.at/doc/relay
* ci: main should be uploaded when docs.yaml changes
* ci: fix typo
* Update .github/workflows/docs-preview.yaml
Co-authored-by: missytake <missytake@systemli.org>
We already require that outgoing connections
use STARTTLS so other servers need a valid TLS
certificate to accept messages from us.
It is then very unlikely that they cannot use TLS
to send messages to us.
Conversely, if they only can send messages to use without TLS,
it likely does not have STARTLS on its port 25
and then we don't want to accept messages from them
because we will likely not be able to reply.
- This is a counterpart to pull request #607. Revised
test_deployed_state() to perform the same error-handling on Git
commands that cmdeploy does. If 'git rev-parse' returns an error,
the value "unknown" is used. If 'git diff' returns an error, the
null string is used.
- This fixes failures in environments where Git is not installed or
where the .git subdirectory is not present (as long as the server
was deployed in the same way).
- test_rewrite_subject() is prone to failure when it checks for the
delivered message, because fetch_all_messages() raises "ValueError:
no messages in imap folder". The check has the potential to happen
before the server has had a chance to deliver the message to the
user's inbox.
- Added a function try_n_times() that attempts to call a function the
specified number of times, with a 1-second sleep between calls. The
call is retried until it doesn't raise an exception. The last call
is made without a 'try' block, so that the final exception passes
through to the caller if it does not return.
- Wrapped call to fetch_all_messages() in try_n_times(), with 5
attempts specified. This should usually allow enough time for the
message to get moved from the postfix queue to the user's inbox.
- test_timezone_env() is producing the warning,
"PytestReturnNotNoneWarning: Test functions should return None, but
src/cmdeploy/tests/online/test_1_basic.py::test_timezone_env
returned <class 'bool'>".
- Revised test_timezone_env() to return None for success instead of
True.
- On a fresh install, if cmdeploy is run the first time with the
--disable-mail option, the echobot invite-link.txt file will not
exist yet.
- Only print the echobot invite link if --disable-mail was not
specified. This fixes the fresh-install error case, and also makes
sense when disabling mail in general, because the echo bot will not
be available at that time.
Stalwart sends `NOTIFY=DELAY,FAILURE`
to request Delivery Status Notifications.
aiosmtpd does not support any parameters,
not just ORCPT, so we have to ignore all of them.
- username_min_length and username_max_length are both set to a
default value of 9 in the chatmail.ini.f template. When they have
the same value, it doesn't matter which one we use in newemail.py
(which handles the /new URL). However, if they are configured to
different values by the admin, then the current implementation using
username_min_length chooses from a smaller set of possible
usernames.
- Revised create_newemail_dict() in newemail.py to use
username_max_length as the length of the random username it offers
via the /new URL. This randomizes within a much larger set of
possible usernames.
- Implemented changes suggested in review by missytake:
- Removed relation between acmetool-redirector and certs.
- Added internal nginx listening on port 8443.
- Changed direction of arrows between certs and the services that
use them. This makes the arrow show the direction of
information flow, rather than a "depends on" relation.
- For filesystem paths, added a descriptive name to the node.
- Replaced most arrows with plain lines, to simply show that a
relationship exists between the two nodes. This also reduces visual
clutter, since the graph is pretty dense with information already.
- Split nginx and certs into two nodes, to reduce entanglement in the
graph. These "linked" nodes are given a different shape and filled
with a different colour, to highlight the fact that they are the
same node.
- Revised text about the meaning of edges in the graph.
- For starters, this file is just a diagram of components of a
chatmail server. In the future, this document can grow into a more
complete description of the architecture of the server, the
deployment process, and the design intent behind what is and isn't
in the code base.
- The name ARCHITECTURE.md is inspired by this article, which also has
good suggestions about what to put in the file:
https://matklad.github.io/2021/02/06/ARCHITECTURE.md.html
- Before proceeding with installation of Python dependencies, check
whether the 'gcc' command is available by running it with the
--version argument. If it is not available, print a helpful message
and exit.
- For the current set of Python dependencies, without GCC, the build
process fails when building the crypt-r package. According to the
error message, on my system the exact command it tries to run is
'x86_64-linux-gnu-gcc', but rather than depend on this variant
specifically, the script checks for the generic 'gcc' command, so as
to avoid coupling the check to an architecture or operating system.
Similar problems arise if we attempt to check for packages by name;
the compiler binary is provided by 'gcc-11', but the symlinks that
provide the unversioned commands (as used by the Python build) come
from a package named 'gcc'. Trying to be too precise in what we
check for could lead to unnecessary failures in some environments,
or become a maintenance challenge in the future. For that reason,
this change simply attempts to run 'gcc' and uses that as a
probably-sufficient proxy for having what the Python package install
will need.
- The Python modules installed by initenv.sh require a compiler to build.
- Revised initenv.sh to check whether build-essential is installed
before proceeding, if the system is based on Debian or Ubuntu.
- Added 'try' blocks around the 'git rev-parse' and 'git diff'
commands that are run in deploy_chatmail(). If there is an error
running rev-parse, git_hash is set to "unknown". If there is an
error running diff, git_diff is set to the null string.
- This allows the deployment process work in two scenarios that would
otherwise fail with an exception:
- Systems where the 'git' command is not available.
- When running with a copy of the tree content of chatmail/relay,
but without a copy of the .git directory.
High-security mode could be configured
to handle more connections by increasing process_limit,
but has problems logging in many users at once after
each Dovecot restart or config reload.
If running in a constrained environment (e.g. an incus / systemd container), setting sysctl limits is constrained, this tweak just checks existing settings and if large enough continues instead of applying
there is another mention of times in privacy.md,
however, there the gist is about that things are deleted,
it is fine if that happens earlier there (also it is not excluded).
targets discussion from https://github.com/chatmail/relay/pull/504
* use "relay" instead of "server" and "address" instead of "account"
* consistent Dovecot capitalization and striking relay in two places
* address missytake's comments: use "chatmail relay servers" sometimes -- it's still fine to talk about relays being, or running on, servers
* draft blocking of incoming non-encrypted mail
* create a new enforceE2EE file in address dirs by default and only accept incoming cleartext file if the enforceE2EE file is missing
* Update cmdeploy/src/cmdeploy/service/filtermail.service.f
Co-authored-by: l <link2xt@testrun.org>
* fix benchmark so they setup encryption
* hack around limitations of aiosmtpd's handliung of RCPTO options
* add tests, and split incoming/outgoing handlers for clarity
* document mailbox directory structure, some streamlining of features/E2EE in intro
* use SMTP response code "523 Encryption Needed"
* filtermail: care for the case that the recipient does not exist
Co-authored-by: missytake <missytake@systemli.org>
* Update chatmaild/src/chatmaild/filtermail.py
Co-authored-by: l <link2xt@testrun.org>
* Update chatmaild/src/chatmaild/filtermail.py
Co-authored-by: l <link2xt@testrun.org>
* remove debug info print
* ensure multipart/report type for mailer-daemon messages
* Allow sending out Autocrypt Setup Messages
---------
Co-authored-by: l <link2xt@testrun.org>
Co-authored-by: missytake <missytake@systemli.org>
* enforce encryption for in-server mails
* make tests work with chatmail server only support e2ee internally
* fix echobot test
* simplify quota-exceeded test
* work around rpc-server fixture changes
- e-mail -> email
- capitalization of software and protocols (Postfix, Dovecot, SMTP, etc)
Rephrased the install instructions to start by recommending the DNS records that cmdeploy is going to check for immediately anyway.
Refactored the migration guide to fix incorrect tar commands (wrong usage of -C flag) and suggest how to copy directly from server to server by logging in with SSH agent forwarding. The mail services should be disabled first before proceeding with any changes and also ensure the echobot and mail spool are copied over to the new server so no messages are lost.
Otherwise email providers which allow to bring your own domain
and use the same IP addresses for all customers
send wildcard certificate instead of the correct one
and Postfix refuses to connect with an error
server certificate verification failed for example.org[A.B.C.D]:25: num=62:hostname mismatch
It does not work because of `smtpd_proxy_filter`
forwarding the message to filtermail
and we cleanup the message once
filtermail reinjects it on port 10025.
I tested with -tls1_2 option
of openssl s_client
that TLS 1.2 connections
are no longer possible
on any ports except port 25.
Port 25 requires at least TLS 1.2
for encrypted connections.
`authclean` cleanup server is used by
reinjecting smtpd running on localhost:10025 by default.
It runs after filtermail
and currently removes `Received` header
to avoid leaking IP address.
Can as well be used to replace `Subject` lines
with `Subject: [...]`.
If there are multiple `Subject` lines,
all of them should be replaced.
This allows us to avoid dealing with
localized subjects, including SecureJoin
messages `vc-request` and `vg-request`
which can have Subject lines like
Subject: =?utf-8?q?Nachricht_von_nrn178fi4=40nine=2Etestrun=2Eorg?=
Replace \r\r\n in literal.eml test with \r\n
to make `test_filtermail_no_literal_packets`
actually reach `check_openpgp_payload()`
and make `check_openpgp_payload()` more strict.
unbound-control is not installed out of the box
and even once installed `flush_zone` does not seem
to work reliably.
Instead of trying to flush the cache from unbound,
we now query authoritative nameserver directly using `dig`.
perform_initial_checks may exit early
and not add `acme_account_url` if required DNS
records are not found.
In this case other `cmdeploy run` fails
with KeyError.
To avoid this, `acme_account_url` should always be set.
Unlike DNS checks, running acmetool
may not fail due to network errors,
so it is more reliable and should be checked first.
I wanted to add `COMPRESS=DEFLATE`,
but it should be added only for sessions
that are logged in because `COMPRESS`
command does not work before logging in.
Dovecot already does it correctly
if we don't overwrite the capability string.
* Improve README for first setup
* DNS: fix flushing DNS when requesting records
* DNS: actually check whether mta-sts record is set correctly
* DNS: add changelog
* DNS: also check for www CNAME record
* DNS: fix tests
* lint: update ruff to 0.6.5 locally
DictProxy can have transactions with the same name
(most frequently `1`) processed in parallel.
Dovecot expects that transaction names on each connection
are independent.
* add allow_ipv6 config option
* add ipv6 config changes to cmdeploy
* fix name of config option for ipv6 in config.py
* move configure ipv6 before service start
* Use templates for disabling ipv6
* lint
* fix parameters in _configure_dovecot
* dont pass domain to _configure_nginx
* make disable_ipv6 boolean
Co-authored-by: missytake <missytake@systemli.org>
* implement namis suggestions reg boolean for ipv6
* Update chatmaild/src/chatmaild/config.py
Co-authored-by: missytake <missytake@systemli.org>
* ruff
* ruff again :)
* fix merge conflict
* CI: add CI machine with IPv6 disabled
* CI: fix sed statement
* CI: fix ubuntu reset
* CI: separate cert storage for staging2 and staging-ipv4
* add DNS records to proper zone
* CI: ignore if folders are missing
* CI: renames are not needed like this
* CI: fix default DNS zone for ipv4
* CI: use debian 12 instead of ubuntu, tired of trying to guess the correct image
* remove duplicared listen on 8443
* use jinja templates for disable_ipv6
* remove unused variable
* add missing % sign in jinja tempalte
* more fun with jinja syntax
* CI: proper rsync paths for acme & DKIM caching
* Changelog: add disable_ipv6 config option
---------
Co-authored-by: missytake <missytake@systemli.org>
Co-authored-by: holger krekel <holger@merlinux.eu>
These additional subjects were extracted from the thunderbird-android
source (which is inherited from k-9).
The extraction was done with:
```
git clone https://github.com/thunderbird/thunderbird-android/
cd thunderbird-android/legacy/ui/legacy/src/main/res
grep string\ name=\"encrypted_subject values-*/strings.xml | cut -f2 -d'>' | cut -f1 -d'<' | sort -u | sed -e 's/^/ "/' -e 's/$/",/'
```
(i did need to clean up one line's escaping to pass the linter's
expectations)
See also https://github.com/thunderbird/thunderbird-android/issues/8011
Otherwise nginx fails when user actually tries to connect,
logs have errors such as
`invalid port in upstream "127.0.0.1:imaps"`
and
`invalid port in upstream "127.0.0.1:submissions"`.
- don't try to guess IP addresses but insist on A and AAAA records
- try to allow ipv4 or ipv6 only zones
- move chatmail.zone generation to jinja so we can have conditionals
in draft-ietf-lamps-header-protection-22, hcp_minimal recommends
"[...]" as the obscured Subject header. In the pending draft
-23 (hopefully released this week, going into a working group last
call), the same HCP will be renamed to hcp_baseline, but it still
recommends the use of "[...]" for the obscured Subject header.
Services are distinguished based on ALPN.
For example,
openssl s_client -connect example.org:443 -alpn smtp
gives SMTP connection and
openssl s_client -connect example.org:443 -alpn imap
gives IMAP connection.
* drastically reduce round-trips for dns checks, and do it during 'run' and 'dns' sub commands
* provide progress-dots for dns checks and "--verbose" for seeing what is executed remotely
* introduce ssh-mediated remote python function execution mechanism
Oversigning (including header name in DKIM-Signature
more times that it appears in the mail) prevents
adding more headers with the same name
without invalidating DKIM signature.
We don't want middleboxes to insert a second From header,
adding Cc field to mails that don't have one etc.
LMTP does not deduplicate messages
if sieve plugin is used.
We don't check for Auto-Submitted header anymore
as iOS application has a notification service
and should not display "You have a new message".
before, the order was 2 - 3.1 - 3.2 - 3
i think, the gist was to have subheadlines under "2.";
this is fixed by this PR.
moreover, the PR contains a small typo fix.
Ensure that first part only contains "Version: 1"
and second part only contains base64 payload
enclosed in "-----BEGIN PGP MESSAGE-----"
and "-----END PGP MESSAGE-----".
Otherwise Dovecot times out when trying to iterate over metadata
of the folder. Apparently it happens when attempting to delete
folder from the server over IMAP.
This change adds XDELTAPUSH capability.
Delta Chat clients detecting this capability
can set /private/devicetoken IMAP metadata
on the inbox to subscribe for Apple (APNS)
notifications.
Notifications are implemented in a new
`chatmail-metadata` service
which handles requests to set /private/devicetoken
IMAP metadata from Delta Chat clients
and /private/messagenew requests from
push_notification_lua script.
To avoid sending notifications for
MDNs, webxdc updates and Delta Chat sync messages,
messages with Auto-Submitted header are ignored
by setting $Auto keyword (flag) on them in Sieve script
and skipping such messages in push_notification_lua script.
Outgoing messages are also ignored.
I installed pyenv and then installed Python 3.9:
$ pyenv install 3.9
$ eval "$(pyenv init -)"
$ pyenv shell 3.9
In a clean repository I ran
$ scripts/cmdeploy init
$ scripts/cmdeploy run
$ scripts/cmdeploy dns
$ scripts/cmdeploy fmt
With the changes made all these commands work.
scripts/cmdeploy test fails some tests
using maildata fixture at
importlib.resources.files(__package__).joinpath("mail-data")
line but this is not critical.
ADSP RFC 5617 is declared historic because of no deployment:
<https://datatracker.ietf.org/doc/status-change-adsp-rfc5617-to-historic/>
However, it is declared as supported by <https://github.com/fastmail/authentication_milter>.
OpenDKIM has a release note from 2014-12-27 saying "Discontinue support for ADSP"
and does not support ADSP anymore.
Anyway, it does not hurt to publish a TXT record
indicating the strictest possible ADSP policy
that we apply to all incoming mail ourselves.
Unlike DMARC which allows either SPF or DKIM to pass,
ADSP requires that DKIM passes.
Testing that envelope FROM matches From: header
already happens in filtermail
and tested with `test_reject_forged_from`.
The most important part here is
`reject_sender_login_mismatch` check
documented in
<https://www.postfix.org/postconf.5.html#reject_sender_login_mismatch>.
OpenDKIM configuration
has two Lua scripts defining strict DKIM policy.
screen.lua filters out signatures that do not correspond
to the From: domain so they are not even checked.
final.lua rejects mail if it is not outgoing
and has no valid DKIM signatures.
OpenDKIM is configured as a milter on port 25 smtpd
to check DKIM signatures
and on mail reinjecting smtpd
to sign outgoing messages with DKIM signatures.
There is no dictionary to set additional attributes,
but admin email can already be retrieved:
? GETMETADATA "" (/shared/admin)
* METADATA "" (/shared/admin {27}
mailto:root@c20.testrun.org)
? OK Getmetadata completed (0.001 + 0.000 secs).
All these entries are related to `postscreen` service
which is currently not enabled.
For documentation see https://www.postfix.org/POSTSCREEN_README.html
If we later want to enable it, we can readd uncommented entries
and document it.
If user types in https://nine.testrun.org/new manually
in the browser, at least Firefox and Brave suggest
to open the app after following the redirect.
This is not secret but makes it easier for mail server admins
to debug why chatmail does not accept their emails.
If the server generates bounce messages, users will also see this
and can redirect to their server support.
It also shows up in /var/log/rspamd/rspamd.log on chatmail server.
Without this option parsing of answer was flaky
as for long records like
_submission._tcp.nine.testrun.org.
dig printed the result with a space rather
than tab as a separator and .split("\t") did not work.
This change makes the `dig` command print the answer
in the form we need so there is no need for complex parsing
other than taking the first line.
`-r` option is added to make sure options are not changed by .digrc
in the root home directory.
This expands the character set used for passwords generated for new
accounts. The set it taken from the set used by the pass tool. The
special characters is the full GNU grep [:punct:] set.
but i am pretty sure this stems from a time where we had non-ephemeral
non-automated account setup (regular testrun.org) and does not apply to chatmail.
If you run it as scripts/initenv.sh,
activating venv is useless as bash will exit immediately afterwards.
If you `source` it as suggested by README.md,
`set -e` will set the flag for the current shell
and your shell will exit as soon as some command returns non-zero status,
e.g. cmdeploy fails or you simply do `ls /foo/bar/baz` and `ls`
complains that `/foo/bar/baz` does not exist.
* create a wwwdev.sh entry point for developing the web part
* rename script
* fix README
* add a note
* don't depend on deltachat python package
* avoid bailing out on jinja2 errors, and provide file-url for instant clickability
* in webdev mode make page auto-refresh every 3 seconds
This does not really matter as Let's Encrypt
supports current CAA `issue` syntax,
but may be useful if more records are added and this flag is copy-pasted.
For reference: <https://www.rfc-editor.org/rfc/rfc8659#name-critical-flag>
Otherwise user may be already created by another connection
as checking if the user exists happens
in a different read-only transaction.
This happens when Delta Chat connects IMAP and SMTP at the same time.
Also update last_login time on login.
* restart dovecot after package replacement (rebase, test condense) <https://github.com/chatmail/relay/pull/913>
* Set permissions on dovecot pin prefs <https://github.com/chatmail/relay/pull/915>
* Route `/mxdeliv/` to configurable port <https://github.com/chatmail/relay/pull/901>
* fix VM detection, automated testing fixes, use newer chatmail-turn and move to standard BIND DNS zone format <https://github.com/chatmail/relay/pull/912>
* Upgrade to filtermail 0.6.1 <https://github.com/chatmail/relay/pull/910>
* pin dovecot packages to prevent apt upgrades <https://github.com/chatmail/relay/pull/908>
* add rpc server to cmdeploy along with client <https://github.com/chatmail/relay/pull/906>
* remove unused deps from chatmaild <https://github.com/chatmail/relay/pull/905>
* set default smtp_tls_security_level to "verify" unconditionally <https://github.com/chatmail/relay/pull/902>
* featprefer IPv4 in SMTP client <https://github.com/chatmail/relay/pull/900>
out.green(f"created config file for {mail_domain} in {inipath}")
defrun_cmd_options(parser):
parser.add_argument(
"--dry-run",
dest="dry_run",
action="store_true",
help="don't actually modify the server",
)
parser.add_argument(
"--disable-mail",
dest="disable_mail",
action="store_true",
help="install/upgrade the server, but disable postfix & dovecot for now",
)
parser.add_argument(
"--website-only",
action="store_true",
help="only update/deploy the website, skipping full server upgrade/deployment, useful when you only changed/updated the web pages and don't need to re-run a full server upgrade",
)
parser.add_argument(
"--skip-dns-check",
dest="dns_check_disabled",
action="store_true",
help="disable checks nslookup for dns",
)
add_ssh_host_option(parser)
defrun_cmd(args,out):
"""Deploy chatmail services on the remote server."""
# config: Warning: service auth { client_limit=1000 } is lower than required under max. load (10200). Counted for protocol services with service_count != 1: service lmtp { process_limit=100 } + service imap-urlauth-login { process_limit=100 } + service imap-login { process_limit=10000 }
# config: Warning: service anvil { client_limit=1000 } is lower than required under max. load (10103). Counted with: service imap-urlauth-login { process_limit=100 } + service imap-login { process_limit=10000 } + service auth { process_limit=1 }
# master: Warning: service(stats): client_limit (1000) reached, client connections are being dropped
default_client_limit = 20000
# Increase number of logged in IMAP connections.
# Each connection is handled by a separate `imap` process.
# `imap` process should have `client_limit=1` as described in
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.