ChatmailDeployer now receives the full config object
so it can access mailboxes_dir and create this directory
during deployment, preventing mail delivery failures.
Capture stderr separately in Incus.run() instead of merging
it into stdout, which corrupted JSON parsing in run_json().
Add --quiet flag to reduce incus noise.
Suppress stderr in Remote subprocess to prevent pytest-xdist
communication issues.
The get_parser() loop that scanned globals() for *_cmd names was fragile
and forced # noqa: F401 on all lxc imports (ruff couldn't see they were
used dynamically).
Replace it with an explicit SUBCOMMANDS list of
(cmd_func, options_func, needs_config) tuples. This makes the full set
of subcommands visible at a glance, their registration order defined,
and the imports unconditionally used (no more noqa suppressions).
Code:
- Fix check_ssh_config_include() to do a case-insensitive line match
(read_text().splitlines() instead of filter(None, map(..., open()))).
- Drop the default help_text= from _add_name_args(); callers now must
supply an explicit string.
- Expand the DNSContainer class docstring.
- Fix sysctl comment: note that incus provides net.* virtualization
so the sysctl only affects the container's network namespace.
Docs (doc/source/lxc.rst):
- Remove double blank line after page title; fix missing comma.
- Replace the plain-text root-access note with a .. caution:: block.
- Tighten the Quick-start section and lxc-test CLI entry.
Replace the two-function find_relay_image / _find_relay_image pair
with Incus.find_image(aliases), which returns the first alias
that exists in the local image store, or None.
Container.launch() passes [RELAY_IMAGE_ALIAS, BASE_IMAGE_ALIAS]
and ensure_base_image() passes [BASE_IMAGE_ALIAS].
Move the policy-rc.d install/remove boilerplate into a shared context
manager in basedeploy.py so both UnboundDeployer and DovecotDeployer use
the same abstraction, and the DNS container's _install_powerdns() inline
shell uses the same pattern.
DovecotDeployer now wraps its three package installs in
blocked_service_startup() to prevent Dovecot from auto-starting on
initial install — avoiding bind conflicts on IPv4-only systems.
Move the Out output-printer class to cmdeploy/util.py so it is shared
across CLI modules. All print/shell calls in lxc/cli.py, lxc/incus.py,
and dns.py now route through Out instead of bare print().
Key additions:
- Out.section() / Out.section_line(): coloured section headers scaled
to the current terminal width (or $_CMDEPLOY_WIDTH for sub-processes).
- Out.shell(): merges stdout/stderr, prefixes each output line, and
prints a red error line with the exit code on failure.
- Out.new_prefixed_out(): indented sub-printer that shares section_timings.
- 'cmdeploy -v / -vv' exposes the verbosity levels.
- Tests for Out added to test_util.py.
After restarting pdns/pdns-recursor, wait up to 10 s for the recursor
to actually answer a query before proceeding. Likewise, after
configure_dns(), poll from inside the relay container until the
configured DNS IP responds.
On --destroy-all, unset the incusbr0 dns.mode and raw.dnsmasq network
options so the next lxc-start starts from a clean bridge state.
DNSConfigurationError (caught in main()) is raised on timeout so the
CLI prints a clean error instead of failing later with a cryptic message.
Add cmdeploy "lxc-test" command to run cmdeploy against local containers,
with supplementary lxc-start, lxc-stop and lxc-status subcommands.
See doc/source/lxc.rst for full documentation including prerequisites,
DNS setup, TLS handling, DNS-free testing, and known limitations.
Apart from adding lxc-specific docs, tests, and implementation files in the cmdeploy/lxc directory,
this PR adds the --ssh-config option to cmdeploy run/dns/status/test commands and pyinfra invocations,
and also to sshexec (Execnet) handling. This allows for the host to need no DNS entries for a relay,
and route all resolution through ssh-config. This is used by the "lxc-test" command, which performs
a completely local setup -- again, see docs for more details.
While working on DNS/SSH things i also unified all zone-file handling
to use actual BIND format as it is easy enough to parse back.
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>