(release-3006.27)=

Salt 3006.27 release notes#

Python 3.11 upgrade#

Starting with Salt 3006.27, the bundled onedir packages (Linux, macOS, and Windows) ship CPython 3.11.15 instead of 3.10.20. Python 3.10 reaches end of security support in October 2026, while the 3006.x series must continue shipping security fixes through July 2027; aligning the bundled interpreter with 3.11 keeps Salt's security cadence on a supported CPython.

Users upgrading from a previous 3006.x onedir package need to reinstall any Salt extensions installed via salt-pip, because the onedir extras-3.10 directory is replaced by extras-3.11. See #69526 / #69527 for details.

Changelog#

Changed#

  • Upgrade the bundled onedir Python from 3.10.20 to 3.11.15 on the 3006.x branch. Python 3.10 reaches end of security support in October 2026, while Salt 3006.x must ship security fixes through July 2027. Users upgrading from a previous 3006.x package will need to reinstall any Salt extensions installed via salt-pip because the onedir extras-3.10 directory is replaced by extras-3.11. #69526

Fixed#

  • Fixed salt-ssh TemplateNotFound when a managed Jinja template imports from another template (e.g. {% from "formula/map.jinja" import x with context %}). SaltCacheLoader now prefers opts["_caller_cachedir"] (the master's cachedir, where the master-side fileclient caches requested files) over opts["cachedir"] (the thin minion's remote path) for its Jinja search path. Backport of the 3007.x/3008.x fix. #31531

  • Fixed the mysql returner ignoring the configured mysql.user from salt-ssh and other contexts where __salt__ lacks config.option. get_returner_options fell back to __opts__ and looked up bare attribute names in it, so the master's top-level user opt (the system user salt runs as, typically root) masked the configured database user and the returner connected as the wrong user. The mysql returner now passes a scoped view of __opts__ containing only mysql.* keys so the lookup cannot collide. #32567

  • Fixed non-deterministic pillar rendering when multiple pillar_roots environments matched the same minion. Pillar.get_tops collected saltenvs into a set and iterated them in hash order, so top-file processing order depended on PYTHONHASHSEED and varied per salt-call invocation. An earlier change made _get_envs return an ordered list, but the caller wrapped the result back into a set. get_tops now uses an insertion-ordered dict so iteration follows pillar_roots config order. #44937

  • Documented the supported approaches for relocating Salt's runtime directories when running rootless: SALT_HOME/SALT_EXTRAS_DIR at install time, root_dir for relative relocation, and the per-key (pki_dir, cachedir, log_file, pidfile, sock_dir) overrides. #55971

  • Rewrote the non-root / unprivileged user configuration page for onedir packaging, consolidating the older overlapping pages and documenting SALT_USER/SALT_HOME/SALT_EXTRAS_DIR, root_dir relocation, and systemd drop-ins. #59955

  • Rewrote the FAQ entry on restarting the minion after upgrade for the onedir packaging era. Removed the broken policy-rc.d/prereq workaround and documented the supported patterns based on KillMode=process in the shipped systemd unit. #61078

  • Updated the packaging docs to explain how to install modules' optional Python dependencies into an onedir install via salt-pip. #64160

  • Documented salt-pip for installing optional Python dependencies into a onedir Salt install, including the extras directory layout, SALT_EXTRAS_DIR relocation, and non-root behavior. #64291

  • Fixed the EC2/cloud metadata grain crashing with KeyError: 'headers' when salt.utils.http.query returns an error response (4xx/5xx with a body, e.g. when the IMDS rejects a recursive sub-path lookup). Since 3006.3 the tornado backend has populated body on HTTPError without also populating headers; the grain now treats the missing headers key as "no Content-Type information" instead of letting the lookup blow up the whole grain load. #65184

  • Updated the non-root user docs for the onedir-era directory layout (/opt/saltstack/salt, extras-3.N, package-managed salt user) and explained how to switch an existing install over to a different account. #65243

  • Expanded the packaging test guide with single-test invocations, environment variables, common failures, and CI parity notes. #65253

  • Fixed master-initiated jobs failing on Python 3.12+ with "There is no current event loop in thread 'Thread-N (_target)'" by installing an asyncio event loop on the SyncWrapper worker thread. #65702

  • Fixed master 4505 publish port becoming unresponsive under load: TCP PubServer now broadcasts to subscribers concurrently so a single slow subscriber no longer stalls the event publisher loop, and the ZeroMQ master PUB socket now enables ZMTP heartbeats so dead subscribers are reaped within seconds instead of waiting for the kernel TCP keepalive. #66282

  • Refreshed the "running as a non-root user" page; replaced outdated 0.9.10-era guidance and added the onedir-aware steps for changing the runtime user. #66353

  • Documented how to install Salt Extensions (saltext.<name>) into an onedir install with salt-pip, and pointed the developer extensions doc at the install instructions. #66524

  • Fixed salt.utils.vmware to use the supported token/tokenType arguments instead of the deprecated b64token/mechanism arguments when calling pyVim.connect.SmartConnect. pyvmomi 9 raises an exception when either deprecated argument is truthy, which broke salt-cloud, the vsphere execution module, and other VMware integrations as soon as pyvmomi was upgraded. #68211

  • Fixed state.event (and salt-run state.event) crashing with UnicodeDecodeError when an event payload contains raw binary bytes such as the DER-encoded certificate returned by x509.sign_remote_certificate. Undecodable bytes are now base64-encoded in the JSON output instead of aborting the runner. #68411

  • Fixed salt.utils.url.create so salt:// URLs built from relative paths round-trip correctly on Python 3.13+, where urllib.parse.urlunparse no longer emits a file:/// prefix for relative paths. salt-ssh file.managed source: salt://... references now resolve as expected on newer-Python targets (e.g. Debian trixie). #68421

  • Fix set_locale on Debian 13/14 where systemd-localed is unavailable; fall back to /etc/default/locale update. #68425

  • Fixed a prereq chain bug where a state at the head of a chain (e.g. state1 -prereq-> state2 -prereq-> state3) would always run when an intermediate state in the chain always produced changes in test mode (e.g. test.succeed_with_changes, module.run), even though the tail state of the chain produced no changes. #68438

  • Fixed Debian salt-minion package failing to upgrade from a non-onedir release. The salt-minion.preinst script assigned an unused PY_VER variable by exec'ing /opt/saltstack/salt/bin/python3, which does not exist when upgrading from a pre-onedir Debian package (e.g. 3006.0+ds-1+240.1). Under set -e this aborted the upgrade with subprocess returned error exit status 127. The unused assignment is removed. #68460

  • Fixed salt-master package upgrades resetting state directory ownership and the debconf salt-master/user value when the master was configured to run as a non-root user. #68577

  • Don't insert local paths before standard library paths in LazyLoader, preventing sys.path reordering when loader modules are already importable. #68755

  • Fixed Salt minion package upgrades when the minion is configured to run as a non-root user via user: in /etc/salt/minion or /etc/salt/minion.d/*.conf. The Debian preinst now reads the configured user before falling back to filesystem ownership, and the rpm pre-minion scriptlet no longer relies on rpm macro directives inside its shell body to communicate the chosen user to the post-minion scriptlet. #68793

  • Fixed a file descriptor leak in the Salt minion: when the single-master sign-in path in Minion.eval_master raised any exception other than SaltClientError (for example OSError from the underlying transport), or when transport: detect rejected a candidate transport because it could not authenticate, the AsyncPubChannel that had been created was not closed, leaking its socket. Minions with unstable network connectivity could exhaust the per-process file descriptor limit. The channel is now always closed on failure via a try/finally. #68901

  • Fixed salt.utils.cache.ContextCache.cache_context writing the serialized pillar context to disk with whatever mode the process umask happened to allow (typically 0o644 on default Linux installs) inside a 0o755 parent directory. Pillar context can carry credentials (passwords, vault tokens, API keys), so any local user could read them; even with the file mode tightened, the directory mode let any local user ls the cache and learn which modules and external-pillar backends were in use. The cache file is now written through tempfile.mkstemp (creates with 0o600 by default) followed by atomic os.replace, and the parent context/ directory is created with stat.S_IRWXU (0o700). #69069

  • Fixed kernelpkg.upgrade on Debian 13 (trixie) and other distros that ship a kernelrelease containing characters outside [\d.-] (for example 6.12.86+deb13-amd64). kernelpkg_linux_apt._kernel_type now parses such releases instead of raising AttributeError: 'NoneType' object has no attribute 'group'. #69131

  • Added a new opt-in auth_retries minion option that caps the AsyncAuth._authenticate() outer retry loop, so a minion that keeps getting retry responses from sign_in() can bail out with SaltClientError instead of looping silently forever. The default is 0 (unlimited), which preserves the existing 3006.x LTS behavior on upgrade; operators who want the new safety cap set auth_retries explicitly to a positive integer. #69442

  • Fixed saltutil.runner/saltutil.wheel failing git-backed master functions (e.g. git_pillar.update) with failed to stat '/root/.gitconfig' when the master runs as a non-root user. Dropping to the master user with chugid left HOME/USER/LOGNAME pointing at the invoking (root) user; these are now aligned with the runas user, and pygit2's cached global-config search path is refreshed. #69569

  • Stopped logging a spurious random_master is True but there is only one master specified. Ignoring. warning once per master at startup for an all-hot multi-master minion. The warning now fires only for a genuinely single-master configuration. #69571

  • Fix OpenNebula salt-cloud documentation to clarify that VM attributes (memory, cpu, vcpu, etc.) must be specified in the profile configuration, not as command-line arguments to salt-cloud -p. #69573

  • Removed bundled MD5/SHA-1 references that tripped FIPS-compliance scanners against the Salt onedir. The cryptography sdist's top-level docs/ directory (which contains Java/Rust test-vector sources naming weak algorithms, e.g. VerifyRSAOAEPSHA2.java) is now pruned from the onedir during pre-archive-cleanup, and the unused __fetch_verify helper in the vendored bootstrap-salt.sh now uses sha256sum instead of md5sum. #69575

  • Fixed salt.utils.atomicfile.atomic_open to fsync the temp file before the atomic rename so a crash after the rename cannot expose a truncated or partial file. #69583

  • Fixed RPM upgrades leaving a previously-running salt-minion service stopped. The %pre minion scriptlet stops the unit so the ownership-restoration chowns don't race a live minion, but the %post / %posttrans scriptlets only called systemctl try-restart - a no-op for an inactive unit. The scriptlets now record the pre-upgrade active state and start the unit unconditionally in %posttrans when the minion was running at the start of the upgrade transaction. #69605

    • Relenv 0.22.16

      • 0.22.15: apply cpython#104135 workaround to bundled ssl.py on Windows

      • 0.22.15: send relenv runtime debug/warning output to stderr (unblocks maturin/pyo3 subprocess consumers)

      • 0.22.16: pin libffi to cpython-bin-deps on Windows #69612

Added#

  • Added tools/audit_doc_links.py and a weekly doc-linkcheck workflow that wrap Sphinx linkcheck, strip the catch-all ignore, and emit a CSV report so external URL regressions in the docs can be tracked without gating PR CI. #60720