(release-3006.26)=
# Salt 3006.26 release notes

<!---
Do not edit this file. This is auto generated.
Edit the templates in doc/topics/releases/templates/
for a given release.
-->


<!--
Add release specific details below
-->

<!--
Do not edit the changelog below.
This is auto generated.
-->
## Changelog

### Removed

- Removed the unmaintained `linode-python` package dependency to stop SyntaxWarnings during install for retired Linode API v3. [#68992](https://github.com/saltstack/salt/issues/68992)


### Changed

- Changed `salt.returners.redis_return` to enumerate the Redis keyspace
    with `SCAN` instead of the blocking `KEYS pattern` command in both
    `get_jids` and `clean_old_jobs`. `KEYS` walks the entire keyspace
    synchronously and stalls the Redis server for the duration; on a
    master with hundreds of thousands of jobs this can block all clients
    of that Redis instance for seconds. `SCAN` is incremental and
    non-blocking. Order of returned keys is no longer guaranteed (the
    returner does not rely on order); operators with custom scripts that
    read `ret:*` or `load:*` directly may see them in a different order. [#69037](https://github.com/saltstack/salt/issues/69037)


### Fixed

- Fixed multi-line scalar variables loaded via `import_yaml` (or `load_yaml`) being rendered as literal `\n` instead of actual newlines when the loaded data is interpolated into a YAML state file (e.g. `- context: {{ data }}`). `PrintableDict.__str__`/`__repr__` now emit string values containing newlines as YAML-safe double-quoted scalars rather than Python `repr()` so they round-trip correctly through the subsequent YAML render pass. [#30690](https://github.com/saltstack/salt/issues/30690)
- Handle requisites correctly for empty SLS files [#30971](https://github.com/saltstack/salt/issues/30971)
- Fixed ``win_pkg`` functions ignoring the ``saltenv`` setting in minion configuration. All public functions (``refresh_db``, ``genrepo``, ``install``, ``remove``, ``list_pkgs``, ``latest_version``, ``upgrade_available``, ``list_upgrades``, ``list_available``, ``version``, ``get_repo_data``, ``get_package_info``) now fall back to ``__opts__["saltenv"]`` when ``saltenv`` is not passed explicitly, instead of always defaulting to ``base``. [#38551](https://github.com/saltstack/salt/issues/38551)
- ``dpkg_lowpkg`` no longer reads ``/var/lib/dpkg/available`` or ``/var/lib/dpkg/info/<package>.list`` directly. It now uses ``dpkg-query`` exclusively, addressing the lintian ``uses-dpkg-database-directly`` warning reported in #52605. ``lowpkg.info`` derives the package install time from dpkg's ``${db-fsys:Last-Modified}`` field instead of the ``.list`` file mtime. [#52605](https://github.com/saltstack/salt/issues/52605)
- Added ``encoding`` parameter to ``file.replace`` execution module and state to support UTF-16, UTF-32, and other multi-byte encoded files that would otherwise be incorrectly treated as binary. [#52793](https://github.com/saltstack/salt/issues/52793)
- Fixed `postgres._find_pg_binary` ignoring `postgres.bins_dir` when a `psql` binary is also present on the system PATH, ensuring the configured `bins_dir` is always preferred over the system PATH. [#53190](https://github.com/saltstack/salt/issues/53190)
- Percent-encode the user and password when adding HTTP basic auth to a URL so reserved characters no longer corrupt the result [#55561](https://github.com/saltstack/salt/issues/55561)
- Fixed a ``SaltCacheError`` ("maximum recursion depth exceeded") raised by the
    etcd data cache when listing an empty folder, which etcd reports as a child of
    itself. The directory walk now stops at the self-referential entry instead of
    recursing indefinitely. [#57377](https://github.com/saltstack/salt/issues/57377)
- Fixed `timezone.system` state always returning `result=False` with "Failed to set UTC to True" on Windows. The hardware clock on Windows is always localtime and cannot be changed, so the UTC/hwclock block is now skipped entirely on Windows. [#57754](https://github.com/saltstack/salt/issues/57754)
- Fixed `archive.tar` placing the `-C <dest>` option after the source/member operands, where tar ignores it. The directory-change option is now emitted before the operands so it takes effect in both create and extract modes. [#57847](https://github.com/saltstack/salt/issues/57847)
- Fixed `OSError: The operation completed successfully` raised by `CreateProcessWithTokenW` on Windows when the underlying advapi32 call fails. The error code is now read from `ctypes.get_last_error()` (the ctypes-saved slot) instead of `win32api.GetLastError()` (the live Windows slot, which may be reset to 0 before it is read). [#57848](https://github.com/saltstack/salt/issues/57848)
- Improved documentation for the `runas` and `password` parameters in `cmd.run`, `cmd.script`, and all `salt.modules.cmdmod` execution functions on Windows. The docs now accurately describe when a password is required: only when the salt-minion is **not** running as SYSTEM or as an elevated Administrator. Removed the inaccurate claim that the target user account must be in the Administrators group. Also changed `cmd.script` to log a warning instead of hard-failing when `runas` is used without a password on Windows, since a password is not always required. [#57951](https://github.com/saltstack/salt/issues/57951)
- Fixed ``pkg.group_installed``/``pkg.group_info`` failing to expand a dnf environment group whose member groups have multi-word names (e.g. ``Group '@Common NetworkManager submodules' not found`` when installing ``Workstation`` on RHEL/AlmaLinux 8, 9 and 10). The member group is now resolved by its bare name when the ``@``-prefixed lookup fails. This affects dnf4 only; dnf5 group handling is unchanged. [#60276](https://github.com/saltstack/salt/issues/60276)
- Fix `tls.create_csr` log message path to use `os.path.join` instead of f-string interpolation so paths render correctly when csr_path has a trailing slash. [#60877](https://github.com/saltstack/salt/issues/60877)
- Fixed the LDAP eauth group-membership lookup re-binding the user on every job
    payload when ``auth.ldap.freeipa`` is enabled. The user is now only re-bound on
    the first payload of a job, matching the standard LDAP code path, so single-use
    2FA credentials (such as a FreeIPA OTP) are no longer consumed more than once. [#61974](https://github.com/saltstack/salt/issues/61974)
- Fixed `SSL: DECRYPTION_FAILED_OR_BAD_RECORD_MAC` errors in the VMware cloud driver by reconnecting when a cached vCenter service instance is found to be stale or corrupted (for example when inherited across a fork by salt-cloud's parallel provider queries). [#61983](https://github.com/saltstack/salt/issues/61983)
- Fix metadata grain so EC2 ``user-data`` is returned verbatim instead of being mangled by the ``=`` line-splitter, which previously corrupted any user-data payload containing ``=`` (e.g. cloud-init ``#cloud-config`` blocks). [#62061](https://github.com/saltstack/salt/issues/62061)
- Fixed LGPO ``get_policy_info`` incorrectly returning a "multiple policies" error when duplicate ADMX policy definitions (e.g. ``TerminalServer.admx`` and ``TerminalServer-Server.admx``) resolve to the same full path. [#62732](https://github.com/saltstack/salt/issues/62732)
- Re-enable test_interrupt_on_long_running_job by removing the initial-onedir-rollout skip marker. [#63627](https://github.com/saltstack/salt/issues/63627)
- Fix missing `dns_plugin_propagate_seconds` arg in acme state/module so DNS propagation timeout is actually forwarded to certbot. [#63700](https://github.com/saltstack/salt/issues/63700)
- Improve PAM eauth diagnostics when ``salt-master`` runs as a non-root user. Previously, ``salt-master``/``salt-api`` running as the ``salt`` user (the 3006.x packaging default) silently failed every PAM authentication with only ``Pam auth failed for <user>:`` in the log; the cause is that the helper subprocess inherits the master's uid and PAM's ``unix_chkpwd`` refuses to validate other users without ``/etc/shadow`` access. The master now emits a one-shot CRITICAL log entry that names the cause and the two standard remediations (run as ``root``, or add the master user to the ``shadow`` group on Debian-derived distributions), and the module documentation describes the constraint. [#64275](https://github.com/saltstack/salt/issues/64275)
- Fixed incorrect minion presence events being sent out on hourly ``Maintenance`` process restarts [#64505](https://github.com/saltstack/salt/issues/64505)
- Catch StrictUndefined in salt jinja custom filters. [#64915](https://github.com/saltstack/salt/issues/64915)
- Stopped logging the misleading "An extra return was detected from minion ... this could be a replay attack" ERROR for benign duplicate returns (also fixes #65516). The local_cache returner now compares a duplicate return to the cached one and logs at DEBUG when the payloads match (the common retry-after-timeout or syndic re-forward case) and at WARNING -- without the "replay attack" wording -- when the payloads differ. [#65301](https://github.com/saltstack/salt/issues/65301)
- Fixed non-root salt CLI access when ``publisher_acl`` or ``external_auth`` is configured. Since 3006.3 the master defaults to running as the ``salt`` user, which left ``sock_dir`` and ``cachedir`` mode ``0o750`` and blocked authorised non-root users from traversing into them to reach ``master_event_pub.ipc`` / ``publish_pull.ipc`` and their per-user ``.<user>_key``. The master now adds the world-execute bit to those two directories when ACLs are configured, without exposing directory listings. [#65317](https://github.com/saltstack/salt/issues/65317)
- Fixed ``salt.ext.tornado.netutil`` import on Python 3.12+ where ``ssl.match_hostname`` was removed and the unmaintained ``backports.ssl_match_hostname`` package is unavailable, which previously broke any Salt master-initiated job (e.g. ``test.ping``, ``state.apply``) on Fedora 39+/Ubuntu 24.04 masters. [#65360](https://github.com/saltstack/salt/issues/65360)
- See #65301 -- the same fix to ``salt/returners/local_cache.py`` quiets the spurious "extra return ... replay attack" ERROR that appeared in multimaster and master-of-masters/syndic setups when the same return arrived more than once. [#65516](https://github.com/saltstack/salt/issues/65516)
- Fix deadlock in parallel `cmd.script` states when the script is served by the master.

    Same fork-inherited ZeroMQ socket race as the `file.managed` fix: a
    `cmd.script` state with `parallel: True` downloads the script via
    `cp.cache_file` in a forked child that inherited the parent's ZeroMQ
    REQ socket, deadlocking the asyncio loop at ~100% CPU. Resolved by the
    same `os.register_at_fork` handlers that drop inherited channel/socket
    references in forked children. [#65709](https://github.com/saltstack/salt/issues/65709)
- Fixed pip.uninstall rejecting the extra_args keyword argument, matching the behavior of pip.install. [#65870](https://github.com/saltstack/salt/issues/65870)
- Fixed salt-ssh failing to fetch ``gitfs_remotes``. ``salt.config.master_config``
    sets ``__fs_update = True`` to suppress fileserver refreshes done by ``FSChan``
    (the master daemon's maintenance thread handles them). salt-ssh inherits the
    master config but has no maintenance thread, so its ``FSClient`` never refreshed
    the fileserver backends and wrappers such as ``cp.list_states`` saw no gitfs
    content until the user ran ``salt-run fileserver.update`` or manually
    ``git fetch``ed the cached repos. ``salt.client.ssh.SSH.__init__`` now removes
    the suppression flag before instantiating ``FSClient`` so gitfs is refreshed
    once at startup. [#66148](https://github.com/saltstack/salt/issues/66148)
- Fixed ``salt/version.py`` reporting the wrong major version on the 3006.x branch when built from a checkout that has no ``salt/_version.txt`` and no usable ``.git`` directory. ``SaltVersionsInfo.current_release()`` now returns the branch's own codename (``Sulfur``) instead of the next un-released codename in the table, so source builds and other tooling no longer leak ``3007.0`` into the reported version. [#67061](https://github.com/saltstack/salt/issues/67061)
- Fixed ``saltutil.runner`` and ``saltutil.wheel`` running master-side functions
    as the minion's user (typically ``root``) instead of the master's configured
    user (the packaged default since 3006 is ``salt``). Running as the wrong user
    left root-owned files in, and tripped git's ``safe.directory`` check on, the
    salt-owned master cache -- breaking, for example, ``git_pillar.update`` invoked
    via ``saltutil.runner``. These functions now drop to the master's configured
    user before executing when invoked from a more-privileged process. [#67716](https://github.com/saltstack/salt/issues/67716)
- Fixed `LocalClient.cmd_subset` raising `TypeError: argument of type 'bool' is not iterable` when one or more targeted minions failed to respond to the `sys.list_functions` probe. Failed minions are now skipped during subset selection. [#68103](https://github.com/saltstack/salt/issues/68103)
- Fixed ``slack_bolt`` engine crashing with ``UnboundLocalError`` when a Slack workflow or other bot posts a message to a monitored channel. Bot messages (``subtype: bot_message``) carry ``bot_id`` and ``username`` instead of a ``user`` field, and these are now used as fallbacks so the engine continues running. [#68105](https://github.com/saltstack/salt/issues/68105)
- Fixed `user.present` to not fail with `result: False` in test mode when a referenced group does not yet exist; the state now reports the pending changes so users can preview states that depend on groups created by a `group.present` requisite in the same run. [#68110](https://github.com/saltstack/salt/issues/68110)
- Fixed ``salt-minion`` and ``salt-proxy`` leaving a privileged (root) keepalive supervisor process at the head of an otherwise unprivileged minion process tree when ``user`` is set to a non-root account. The supervisor now drops privileges to the configured user once the keepalive child has been spawned. [#68115](https://github.com/saltstack/salt/issues/68115)
- Fixed ``ValueError: Formatting field not found in record: 'colorlevel'`` errors when ``log_fmt_console`` uses custom color attributes such as ``%(colorlevel)s`` or ``%(colormsg)s``. ``SaltLogRecord`` now always provides the ``color*`` attributes (uncolored by default) so that log records buffered by the temporary deferred stream handler can be formatted by a colorized console formatter once it is installed. [#68129](https://github.com/saltstack/salt/issues/68129)
- Fixed ``salt-call`` silently ignoring ``--file-root``, ``--pillar-root``, and ``--states-dir`` when ``--local`` was not passed. These overrides only affect the local minion config and are clobbered by the master's values via the remote file client, so ``salt-call`` now emits a warning explaining that ``--local`` is required for the override to take effect. [#68137](https://github.com/saltstack/salt/issues/68137)
- Fixed event signature verification failing under ``minion_sign_messages``. The minion was signing the return load before ``salt.channel.client.AsyncReqChannel._package_load`` attached transport metadata (``nonce``, ``ts``, ``tok``, ``id``), so the bytes the master re-serialized to verify did not match what was signed and every signed return was dropped. Signing is now performed inside ``_package_load`` after the metadata is attached, against the same bytes the master verifies. [#68181](https://github.com/saltstack/salt/issues/68181)
- Fixed ``pkgrepo.managed`` honouring ``clean_file: True`` when the desired
    repo line is already present in the managed file alongside unrelated stale
    lines. Previously the state returned "already configured" and silently
    skipped both the file truncation and the re-write, leaving the stale
    entries (for example an obsolete ``bullseye-backports`` line in a file
    managed for ``bookworm-backports``) in place. The clean + reconfigure
    path now runs whenever the managed file contains any non-comment,
    non-blank content other than the desired repo line; when the file already
    contains only the desired line the state remains idempotent. [#68208](https://github.com/saltstack/salt/issues/68208)
- Fixed ``pkg.group_installed`` reporting failure on RPM-based systems when a package group's default or optional members are not available in any enabled repository. The state now only considers mandatory group members and explicitly requested ``include`` packages when checking for install failures, matching the behavior of ``yum/dnf group install`` (which reports "No match for group package" but still exits 0). [#68210](https://github.com/saltstack/salt/issues/68210)
- Pass ``--disable-pip-version-check`` when ``pip.list``, ``pip.freeze``, ``pip.list_upgrades``, ``pip.upgrade``, and ``pip.list_all_versions`` invoke pip, so these calls no longer hang for ~20s per invocation on airgapped minions while pip tries to reach PyPI for its self-version check. [#68214](https://github.com/saltstack/salt/issues/68214)
- Fixed ``archive.extracted`` failing to enforce ``user``/``group`` ownership on archives whose tar/zip members include no explicit directory entries (e.g. Oracle's GraalVM JDK tarballs). ``archive.list`` now derives the top-level directory from the common prefix of file and link members in addition to dir members, so ownership is applied to the extracted top-level directory in all cases. [#68227](https://github.com/saltstack/salt/issues/68227)
- Fixed deltaproxy sub-proxies returning identical grain data for every controlled minion. ``subproxy_post_master_init`` now re-packs each sub-proxy's freshly loaded per-minion grains into its execution-module, returner, executor and proxy LazyLoaders so ``__grains__`` inside loaded modules reflects that sub-proxy's device instead of the placeholder values captured during the first-pass grains load through the control proxy. [#68248](https://github.com/saltstack/salt/issues/68248)
- Fixed the salt-minion (and salt-api, salt-cloud, salt-master, salt-syndic) Debian postinst scripts hanging or erroring with "Bad file descriptor" when run from a non-interactive Debian preseed late_command chroot, by tearing down the debconf protocol with ``db_stop`` and explicitly closing file descriptor 3 before the auto-generated ``#DEBHELPER#`` section runs. [#68269](https://github.com/saltstack/salt/issues/68269)
- Fixed ``file.managed`` failing with ``WinError 123`` on Windows when caching a remote URL whose path embeds another URL (e.g. an archive.org snapshot of an ``https://...`` resource). The URL-path portion of the ``extrn_files`` cache path is now sanitised the same way the network location already is. [#68273](https://github.com/saltstack/salt/issues/68273)
- Fixed ``logrotate.set`` dropping the second ``endscript`` (and turning
    embedded shell commands into bogus setting keys) when a stanza contained
    multiple script blocks such as both ``prerotate`` and ``postrotate``. Script
    directives are now parsed as opaque multi-line bodies and round-trip with
    their own ``endscript`` terminator each. [#68293](https://github.com/saltstack/salt/issues/68293)
- Fixed the `salt.state` orchestrate state silently reporting only `Run failed on minions: <minion>` when a targeted minion returned `False`, no return at all, or a list of error strings. The orchestrate comment now includes the per-minion failure detail (the minion's actual return value or "did not return a state result") so operators can diagnose `salt-run state.orchestrate` failures without re-running with extra logging. [#68326](https://github.com/saltstack/salt/issues/68326)
- Fixed worker process crash when salt is used outside CLI tools. [#68332](https://github.com/saltstack/salt/issues/68332)
- Fixed ``clean_old_jobs`` in the default local job cache returner to use the jid file's modification time (``st_mtime``) instead of the inode change time (``st_ctime``). A package upgrade's ``chown -R /var/cache/salt/master`` resets ``st_ctime`` on every existing jid file, which previously made the maintenance process treat every pre-upgrade job as freshly created and prevented cleanup until ``keep_jobs_seconds`` had elapsed. On busy masters this exhausted the partition's inodes within a day. [#68351](https://github.com/saltstack/salt/issues/68351)
- Fixed the ``proxmox`` salt-cloud driver raising ``Could not determine an IP address to use`` before the VM was created and started. The IP address is now determined after the VM is running, and the running VM's address reported by Proxmox is used as a fallback when neither a static ``ip_address`` nor ``agent_get_ip`` is configured. [#68353](https://github.com/saltstack/salt/issues/68353)
- Changed ``KillMode`` in the shipped ``salt-minion.service`` systemd unit from ``process`` to ``mixed`` so that ``systemctl stop`` / ``systemctl restart salt-minion`` no longer leaves orphaned ``Minion._thread_return`` worker processes outside the cgroup. SIGTERM is still sent only to the main PID (so the job return scheduled by ``service.restart salt-minion`` from #68183 has time to finish), but any remaining children are reaped with SIGKILL after the main process exits or ``TimeoutStopSec`` elapses. [#68406](https://github.com/saltstack/salt/issues/68406)
- Fixed `task.edit_task` on Windows rejecting `restart_count=999` even though the documented and error-message-stated maximum is 999. The validation now accepts the full 1..999 range. [#68419](https://github.com/saltstack/salt/issues/68419)
- Fixed ``win_task.add_trigger`` so that ``repeat_duration="Indefinitely"`` actually produces an indefinite repetition pattern. Previously the empty string from the internal duration lookup was assigned to ``Repetition.Duration``, which the Windows Task Scheduler treats as "0 seconds" and silently disables repetition. The Duration property is now left at its default for the "Indefinitely" case, which is the documented way to repeat forever. [#68420](https://github.com/saltstack/salt/issues/68420)
- Fixed ``user.setpassword`` on Windows reporting success (``retcode: 0``) when the target user does not exist. The execution module now returns ``False`` and logs an error in that case, so callers and the ``user.present`` state correctly detect the failure instead of swallowing the Win32 "user name could not be found" message as a successful return. [#68428](https://github.com/saltstack/salt/issues/68428)
- Fixed ``user.present`` on Windows so it actually updates a user's password
    when the existing password differs from the one specified in the state.
    Previously the state reported "User is already present and up to date" and
    left the password unchanged. [#68429](https://github.com/saltstack/salt/issues/68429)
- Stop salt-ssh state runs from clobbering the master-side fileclient ``cachedir`` with the on-target ``thin_dir`` cachedir. The state fileserver cache for salt-ssh state runs is now written under the configured master ``cachedir`` (e.g. ``/var/cache/salt/master/``) instead of under the minion's thin_dir path on the master filesystem. [#68458](https://github.com/saltstack/salt/issues/68458)
- Fixed ``pkg.add_repo_key`` and ``pkgrepo.managed`` so APT keyring files that target an ``.asc`` destination keep their ASCII armor instead of being dearmored, matching the apt-secure(8) convention and allowing armored keyfiles that bundle multiple keys to be installed even when the ``gpg`` binary is not available on the minion. [#68464](https://github.com/saltstack/salt/issues/68464)
- Fixed ``jobs.list_jobs search_metadata`` so it matches jobs whose metadata
    was passed as a CLI keyword argument (e.g. ``state.apply metadata={...}``)
    and is therefore carried inside the job's ``Arguments`` rather than at the
    top of the job payload. [#68481](https://github.com/saltstack/salt/issues/68481)
- Fixed `lgpo.set` state reporting "Failed to set the following policies" on subsequent runs of policies with sub-elements (e.g. Storage Sense thresholds). The state compared a user-supplied dict keyed by element id with a current dict keyed by the ADML display name; both forms now normalize to the canonical element id before comparison so the state is idempotent. [#68489](https://github.com/saltstack/salt/issues/68489)
- Fixed minion rejecting the master with "Invalid master key" after restart when the cached `minion_master.pub` differs from the master's payload pub_key only in trailing whitespace. `AsyncAuth.verify_master` now normalizes both sides through `clean_key` before comparing and caches the normalized form on first contact. [#68493](https://github.com/saltstack/salt/issues/68493)
- Fixed ``TypeError: 'NoneType' object is not iterable`` raised from ``AsyncReqMessageClient._send_recv`` when a per-message timeout completes the future before the send/receive coroutine catches a transient transport exception, which aborted the minion's connect loop and prevented it from connecting to the master. [#68506](https://github.com/saltstack/salt/issues/68506)
- Fixed ``docker_network.present`` recreating networks on every run against Docker 29+. Docker 29 added an empty ``IPRange`` field to every IPAM Config entry; ``docker.compare_networks`` now drops empty/None placeholder values before comparing pools, and the state's default-pool short-circuit treats the empty field as absent. [#68518](https://github.com/saltstack/salt/issues/68518)
- Fixed `pkg.installed` verification on x86_64 hosts that mix `x86_64` and `x86_64_v2` packages (e.g. AlmaLinux 10.1). `salt.utils.pkg.rpm.resolve_name` and `salt.modules.yumpkg.normalize_name` now treat `x86_64_v2` as compatible with `x86_64` instead of appending the arch suffix, so installed packages match the names Salt records. [#68540](https://github.com/saltstack/salt/issues/68540)
- Fixed ``mysql_grants.present`` reporting "Failed to execute" when granting ``ALL PRIVILEGES`` on ``*.*`` against MySQL 8.4+, where the server's privilege set drifted from Salt's hard-coded list (``SET_USER_ID`` removed, many dynamic privileges added). ``grant_exists`` now derives the expected privilege set from the connected server's ``SHOW PRIVILEGES`` output instead of a static list. [#68567](https://github.com/saltstack/salt/issues/68567)
- Fixed ``cp.get_template`` raising ``AttributeError: 'NoneType' object has no attribute 'get'`` when the Jinja template uses ``{% from '...' import ... with context %}``. The cp module's loader-backed ``__opts__`` is now unwrapped to a plain dict before the SaltCacheLoader instantiates the file client and channel that fetch the imported template. [#68572](https://github.com/saltstack/salt/issues/68572)
- Fixed `ImportError: cannot import name 'wait' from partially initialized module 'multiprocessing.connection'` raised during salt-master/minion shutdown when a reentrant SIGTERM hit `ProcessManager.kill_children()` mid `Process.join(0)`. `salt.utils.process` now eagerly imports `multiprocessing.connection` so the module is fully initialised before any signal handler can trigger its lazy import. [#68573](https://github.com/saltstack/salt/issues/68573)
- Fixed `cmd.script` on Windows raising `Invalid user: <runas>` when `runas` is a domain account (`DOMAIN\user`, `user@DOMAIN`, or a SID). The pre-execution `user.info` check is backed by `NetUserGetInfo` which only resolves local-machine accounts and returns empty for many valid domain users; the missing lookup is now logged as a warning and execution continues so the underlying `win_runas` machinery can authenticate the account. [#68578](https://github.com/saltstack/salt/issues/68578)
- Fixed `pkg.install` on Windows silently downgrading the salt-minion when a numeric `version=` argument was passed (e.g. `version=3007.10` was YAML-parsed to the float `3007.1` and then matched the wrong winrepo entry). When the numeric version uniquely matches a string-keyed winrepo entry it is now resolved to that entry; when it is ambiguous (e.g. both `3007.1` and `3007.10` are in the winrepo) the install is refused with a clear error pointing the user at the quoted-version syntax. [#68620](https://github.com/saltstack/salt/issues/68620)
- Fixed the loader masking failure reasons when multiple modules declare the same `__virtualname__` and each `__virtual__()` returns False, so users now see every reason (e.g. both x509 v1's "Superseded, using x509_v2" and x509_v2's "Could not load cryptography") instead of only the first one recorded. [#68625](https://github.com/saltstack/salt/issues/68625)
- Fix `NetapiClient.runner` raising `TypeError` when `timeout` arrives as a string from the salt-api HTTP form. [#68653](https://github.com/saltstack/salt/issues/68653)
- Fixed `master_job_cache: redis_return` raising `KeyError: 'redis_return.prep_jid'` by registering the `redis` returner under both `redis` and `redis_return` virtual names, matching the documented `--return redis_return` usage and the module's file name. [#68663](https://github.com/saltstack/salt/issues/68663)
- Fixed ``ini.options_present`` with ``strict: True`` to remove sections that are present in the ini file but absent from the supplied ``sections`` mapping. [#68673](https://github.com/saltstack/salt/issues/68673)
- Handle `SaltDeserializationError` in grains cache loading so a corrupted cache file no longer propagates as CRITICAL during minion startup. [#68678](https://github.com/saltstack/salt/issues/68678)
- Fixed ``network.interfaces`` on Windows systems falling back to WMI (i.e. .NET older than 4.7.2): the default gateway is now reported under ``gateway`` instead of being mistakenly emitted as ``broadcast``. [#68692](https://github.com/saltstack/salt/issues/68692)
- Fixed ``file.managed`` (and other template-rendering callers) silently overwriting user-supplied ``slspath``, ``sls_path``, ``slsdotpath`` and ``slscolonpath`` values in ``defaults``/``context`` with values regenerated from the caller's ``sls`` key. [#68754](https://github.com/saltstack/salt/issues/68754)
- Fixed ``env_order`` not being honored when merging pillar data across environments. ``Pillar.render_pillar`` now iterates matched environments in the configured ``env_order`` so that, with ``top_file_merging_strategy: merge_all``, the last environment in ``env_order`` wins on conflicting pillar keys instead of the result depending on dict insertion order. [#68785](https://github.com/saltstack/salt/issues/68785)
- Improved the "Malformed topfile" error from ``HighState.verify_tops`` to name the saltenv and the matcher whose state declarations were not formed as a list, so users can locate the offending entry in their ``top.sls``. [#68792](https://github.com/saltstack/salt/issues/68792)
- Removed orphaned GnuPG dotlock files (``.#lk<addr>.<host>.<pid>``) from ``gpg_keydir`` before each decrypt in the ``gpg`` renderer so they no longer accumulate when a gpg subprocess is killed mid-operation. [#68869](https://github.com/saltstack/salt/issues/68869)
- Fix `pkg.installed` idempotency on FreeBSD when `with_origin=True` causes
    `pkg.list_pkgs` to return per-package dicts instead of version lists; extract
    the version list before version-string comparison so a second state run no
    longer falsely reports packages as changed. [#68886](https://github.com/saltstack/salt/issues/68886)
- Fix gen_signature() signing raw pub key content instead of clean_key'd content, causing master_use_pubkey_signature verification to always fail. [#68930](https://github.com/saltstack/salt/issues/68930)
- Fixed spurious ``FileLockError: lock_fn ... exists and is not a file`` raised by ``salt.utils.files.wait_lock`` and ``salt.utils.files.await_lock`` (and therefore by ``state.apply`` queue locking) when another process removed the lock file between the two separate ``os.path.exists`` / ``os.path.isfile`` stats. The pre-check now uses a single ``os.stat`` call so a transient regular-file lock no longer trips the "not a file" branch. [#68931](https://github.com/saltstack/salt/issues/68931)
- Fixed pkg.installed(update_holds=True) for APT multiarch packages by preserving arch-qualified package names through install target parsing and verification. [#68932](https://github.com/saltstack/salt/issues/68932)
- Fix deadlock in parallel `file.managed` states when source is served by the master.

    Forked parallel-state children previously inherited the parent's ZeroMQ
    REQ socket and asyncio loop from `salt.fileclient.RemoteClient`,
    `salt.crypt.AsyncAuth/SAuth`, and `salt.utils.event.SaltEvent`.  Multiple
    sibling children racing those handles deadlocked the asyncio loop with
    ~98% CPU and never completed.  Salt now registers `os.register_at_fork`
    handlers on those classes that drop inherited channel/socket references
    in any forked child; the next use rebuilds them fresh. [#68940](https://github.com/saltstack/salt/issues/68940)
- Fixed grain and pillar targeting matching minions whose data cache entry was missing. ``CkMinions._check_cache_minions`` now excludes accepted minions that have no cached grains/pillar data from greedy target results, instead of silently including them as candidates. [#68976](https://github.com/saltstack/salt/issues/68976)
- Avoid AttributeError on a closed IPCClient when the connect coroutine resolves after close(). [#68993](https://github.com/saltstack/salt/issues/68993)
- Fixed `salt.utils.network.sanitize_host` stripping colons from IPv6 addresses, which broke `network.ping` and any other caller that passed an IPv6 host. [#68995](https://github.com/saltstack/salt/issues/68995)
- Added support for MAINTAIN (m) privilege introduced in PostgreSQL 17 to salt.modules.postgres and salt.states.postgres_privileges [#69003](https://github.com/saltstack/salt/issues/69003)
- Fixed `redis.get_master_ip` silently dropping the `password` argument. The
    function was forwarding its arguments positionally to `_connect`, but
    `_connect`'s third positional slot is `db`, not `password`, so the
    caller's password landed in the database-index argument and the actual
    password fell through to `config.option("redis.password")`. Arguments
    are now passed by keyword. [#69029](https://github.com/saltstack/salt/issues/69029)
- Fixed `salt.modules.redismod._connect` rejecting valid `db=0`. The helper
    used a truthy check (`if not db`) to decide whether to fall back to
    `config.option("redis.db")`, but `not 0` is `True`, so an explicitly
    supplied `db=0` was silently replaced by the configured value. The check
    is now `if db is None`, matching the pattern already used by the sibling
    `_sconnect` helper in the same module. Other arguments keep their
    truthy-check semantics on purpose. [#69030](https://github.com/saltstack/salt/issues/69030)
- Fixed two distinct bugs in the `salt.engines.redis_sentinel` engine that
    together prevented it from being usable. `start()` no longer raises
    `AttributeError: 'dict_values' object has no attribute 'pop'` on Python 3
    (the dict.values() result is now wrapped in `list(...)`). `Listener` and
    `start()` now accept an optional `password` argument and forward it to
    the redis client, allowing the engine to authenticate against a Sentinel
    that requires AUTH; the default of `None` keeps existing configurations
    working unchanged. [#69031](https://github.com/saltstack/salt/issues/69031)
- Fixed `salt.returners.redis_return` silently ignoring the documented
    `redis.password` configuration option. The returner now reads
    `redis.password` from config (in both regular and proxy modes) and
    forwards it to both the single-server `redis.StrictRedis` and the
    `StrictRedisCluster` constructors. Operators with auth-protected Redis
    no longer lose every job return to a hidden `NOAUTH Authentication
    required` failure; deployments without a password are unaffected. [#69032](https://github.com/saltstack/salt/issues/69032)
- Fixed three closely-related bugs in `salt.cache.redis_cache` that
    together broke hierarchical-bank semantics:
    `_build_bank_hier` now registers each child bank name in both the
    parent's `$BANK_` set (consumed by `flush()` tree traversal) and the
    parent's `$BANKEYS_` set (consumed by `list_()`); `_get_banks_to_remove`
    now decodes the bytes returned by `smembers` and skips the `"."`
    placeholder, so recursive `flush()` of a parent bank actually descends
    into sub-banks instead of corrupting the path; and `flush(bank)` of a
    sub-bank now removes the flushed bank's own reference from its
    parent's index sets so `list_(parent)` no longer reports it as
    present. Together these fixes restore `cache.list("minions")`,
    `salt-run manage.present` and `salt-run manage.up` for masters
    configured with `cache: redis`. [#69033](https://github.com/saltstack/salt/issues/69033)
- Fixed `salt.tokens.rediscluster` being unable to retrieve any eauth
    token. The cluster client was created with `decode_responses=True`,
    which caused `redis_client.get()` to return `str` and broke
    `salt.payload.loads` (msgpack rejects `str`); it also caused
    `redis_client.keys()` to return `str` and broke
    `[k.decode("utf8") for k in ...]` (`str` has no `.decode`). Both
    errors were swallowed by broad `except Exception` handlers, so eauth
    appeared to silently reject every token. `decode_responses=True` is
    removed; values now round-trip as bytes through msgpack as the rest
    of the module already expected. [#69035](https://github.com/saltstack/salt/issues/69035)
- Fixed `salt.returners.redis_return` leaking `<minion>:<fun>` last-jid
    pointer keys indefinitely. The pointer was written with `pipeline.set`
    and no `ex=` TTL, so any (minion, fun) pair that stopped running stuck
    in Redis forever -- O(minions × distinct funcs) keys accumulating over
    the lifetime of the master. The pointer now expires on the same TTL
    as the rest of the returner data (`keep_jobs_seconds`). Operators with
    external scripts reading these keys directly may observe them
    expiring; the documentation never promised they would not. [#69038](https://github.com/saltstack/salt/issues/69038)
- Fixed `salt.returners.redis_return.get_fun` always returning an
    empty dict. The function read return data from a `<minion>:<jid>`
    key that no other code in the module ever wrote -- a leftover from
    an older storage schema. It now reads from the canonical
    `ret:<jid>` hash via `HGET ret:<jid> <minion>`, matching the
    storage layout that `returner` actually produces and the read
    pattern that `get_jid` already uses. [#69039](https://github.com/saltstack/salt/issues/69039)
- Fixed `salt.returners.pgjsonb` writing database errors to `sys.stderr`
    instead of Salt's logger. Errors from `_get_serv`, `_purge_jobs` and
    `_archive_jobs` are now reported via `log.exception`, so they reach
    the configured `log_file` / syslog destination on a daemonized master,
    including a full traceback. The unused `import sys` is also dropped. [#69048](https://github.com/saltstack/salt/issues/69048)
- Fixed `salt.returners.pgjsonb.returner` letting any non-connection
    `psycopg2.DatabaseError` propagate to the caller — including the
    syndic-aggregate publish path in `salt/master.py` which had no outer
    catch — so a single bad row could escape into a master subprocess.
    `event_return` had no error handling at all and a database failure
    during a flush propagated similarly. Both functions now catch
    `SaltMasterError` and `psycopg2.DatabaseError` locally, log a
    contextual message (jid/id for returns, batch size for events), and
    drop the affected payload. While here, fix `event_return` passing
    the events list as the positional `ret` argument to `_get_serv`,
    which was a copy-paste leftover from `returner(ret)`. [#69058](https://github.com/saltstack/salt/issues/69058)
- Fixed `salt-api`'s `/events` endpoint accepting eauth tokens via query
    string (``?token=...`` or ``?salt_token=...``). Tokens supplied that
    way end up in HTTP access logs, the browser ``Referer`` header, log-
    aggregation systems and shell history; the token retains validity for
    ``token_expire`` (12h by default), so any party reading those logs can
    replay the token. The endpoint now rejects query-string tokens with a
    400 error pointing at the ``X-Auth-Token`` header (for non-browser
    clients) or the session cookie established by ``/login`` (for browser
    ``EventSource`` clients) as the supported channels. ``X-Auth-Token``
    header support is added; cookie-based auth continues to work
    unchanged. [#69071](https://github.com/saltstack/salt/issues/69071)
- ``LoadAuth.get_tok`` now distinguishes between corrupt token blobs (removed from the store) and transient backend errors such as Redis connection drops or NFS hangs (token kept, request treated as not-authenticated). Previously a single backend hiccup could log every authenticated user out by deleting valid tokens. [#69073](https://github.com/saltstack/salt/issues/69073)
- ``cmd.run`` and friends no longer include the ``env`` and ``stdin`` arguments in the ``CommandExecutionError`` raised when the underlying subprocess fails to start (typically ``ENOENT`` / binary not found). Both fields routinely carry credentials passed in by the caller (``env={"DB_PASSWORD": "..."}``, password piped via ``stdin``), and the error message ends up in master/minion logs and in event-bus return data visible to the API caller. [#69075](https://github.com/saltstack/salt/issues/69075)
- Lowered the "Cache version mismatch clearing" log message in ``salt.utils.cache.verify_cache_version`` from ``WARNING`` to ``DEBUG``; the cache is rebuilt as part of normal operation after upgrades or when an ephemeral cache directory has been removed, and does not warrant user attention. [#69106](https://github.com/saltstack/salt/issues/69106)
- * Relenv 0.22.14
      - Update sqlite to 3.53.2.0
      - Update openssl to 3.5.7 [#69129](https://github.com/saltstack/salt/issues/69129)
- Surface the real cause of a proxymodule load failure in salt-proxy's abort message. The misleading "Proxymodule X is missing an init() or a shutdown() or both" wording is now only used when init/shutdown really are missing from a loaded module; if the module failed to load (for example because its ``__virtual__`` returned False), the underlying reason is included in the error. [#69139](https://github.com/saltstack/salt/issues/69139)
- Fixed ``pkg.hold`` and ``pkg.list_holds`` on dnf5 systems (e.g. Fedora 42+):
    ``pkg.hold`` now calls ``dnf5 versionlock add <pkg>`` (the bare
    ``versionlock <pkg>`` form was rejected by dnf5), and ``pkg.list_holds``
    reads ``/etc/dnf/versionlock.toml`` directly so ``pkg.installed`` with
    ``hold: true`` is again idempotent. [#69181](https://github.com/saltstack/salt/issues/69181)
- Fixed Salt-SSH syncing internal modules as extmods [#69199](https://github.com/saltstack/salt/issues/69199)
- Fixed ``lgpo_reg.value_absent`` failing when the Registry.pol entry was already absent but the registry value still existed. ``lgpo_reg.delete_value`` was returning early before reaching the registry cleanup code, causing the state to see no changes and report failure. The registry value is now removed regardless of whether the pol entry was present. [#69203](https://github.com/saltstack/salt/issues/69203)
- Fixed `postgres_local_cache.save_load` raising `psycopg2.errors.UniqueViolation` when more than one master in an active-active multi-master cluster persists the same JID; the INSERT is now idempotent via `ON CONFLICT (jid) DO NOTHING` on PostgreSQL >= 9.5, and the duplicate is tolerated on older servers. [#69214](https://github.com/saltstack/salt/issues/69214)
- Fixed Windows MSI self-upgrade via ``pkg.install`` failing with error 1603. The old product's ``DeleteConfig_DECAC`` custom action was unconditionally deleting ``ROOTDIR\var`` during ``RemoveExistingProducts``, destroying the MSI that ``pkg.install`` had cached to ``ROOTDIR\var\cache`` before launching the upgrade. Users who had ``REMOVE_CONFIG=1`` persisted in the registry (from checking "On uninstall" at install time) hit a worse variant where the entire ``ROOTDIR`` was deleted. The fix checks ``UPGRADINGPRODUCTCODE`` — set by Windows Installer whenever an uninstall is triggered by a major upgrade — and skips all ``ROOTDIR`` deletion during upgrades, matching the behaviour of the NSIS installer which has always preserved ``ROOTDIR`` during upgrades. [#69219](https://github.com/saltstack/salt/issues/69219)
- Fixed `TypeError: string indices must be integers` in the minion when the master returns a bare string error response (e.g. `"bad load"`, `"Some exception handling minion payload"`) for a pillar request. The minion now raises a clean `AuthenticationError` instead of crashing, allowing the caller to retry or fail gracefully. [#69228](https://github.com/saltstack/salt/issues/69228)
- pkg.list_patches in yumpkg.py parses tdnf output on Photon OS [#69229](https://github.com/saltstack/salt/issues/69229)
- Fix `git.tag` so that the documented `message` argument is actually forwarded to `git tag`, creating an annotated tag with the supplied message instead of silently producing a lightweight tag. [#69298](https://github.com/saltstack/salt/issues/69298)
- Fixed `salt.auth.pam` conversation callback so it answers `PAM_PROMPT_ECHO_ON` prompts with the supplied username; previously only `PAM_PROMPT_ECHO_OFF` prompts were answered, which caused `pam_authenticate` to silently fail (and salt-api to return 401) against PAM stacks that re-prompt for the user. [#69304](https://github.com/saltstack/salt/issues/69304)
- Ensure multiple masters have their own job/state queues [#69308](https://github.com/saltstack/salt/issues/69308)
- Fixed loading private keys from PKCS#12 containers with x509_v2 [#69312](https://github.com/saltstack/salt/issues/69312)
- Fixed creating self-signed PKCS#12-encoded certificates [#69319](https://github.com/saltstack/salt/issues/69319)
- Fixed minion state queue replacing the master-assigned JID on queued state runs, so returns now come back tagged with the JID the master actually published. [#69386](https://github.com/saltstack/salt/issues/69386)
- Made the salt user's home directory and the relenv ``extras-<py-ver>`` directory configurable in the Linux packaging. The DEB preinst scripts now source ``/etc/default/salt-setup`` (and ``/etc/sysconfig/salt-minion-setup`` for cross-distro parity with RPM) before applying the ``SALT_HOME``/``SALT_USER``/``SALT_GROUP``/``SALT_NAME`` defaults, mirroring the long-standing RPM behavior. A new ``SALT_EXTRAS_DIR`` override is honored by both stacks so the extras tree can be relocated outside ``/opt/saltstack/salt`` and its ownership is correctly restored on upgrade. [#69402](https://github.com/saltstack/salt/issues/69402)
- Fixed minion worker threads hanging or crashing when returning job results
    to the master. The main process now fires an error event back to the worker
    when ``req_channel.send()`` times out, so workers wake up immediately rather
    than waiting out their full timeout. Replaced the bare ``TimeoutError`` raised
    in ``_send_req_sync`` with ``SaltReqTimeoutError`` so ``_return_pub``'s existing
    handler catches it correctly. The worker's wait timeout is now derived from
    ``return_retry_timer_max * return_retry_tries`` to ensure it always outlasts
    the main process's retry budget. [#69416](https://github.com/saltstack/salt/issues/69416)
- Fixed zsh completion by using the proper python3 instead of python2. [#69419](https://github.com/saltstack/salt/issues/69419)
- Fixed Photon OS Arm64 FIPS CI by re-enabling the OpenSSL default provider after installing openssl-fips-provider, working around the disabled-default-provider bug in `openssl-fips-provider <= 3.1.2-3.ph5` on the lagging Photon aarch64 mirror. [#69449](https://github.com/saltstack/salt/issues/69449)
- Add regression test for changelog template multi-line rendering and harden template with indent filter so continuation lines are correctly indented under the bullet (defensive backport of #69458 to 3006.x). [#69454](https://github.com/saltstack/salt/issues/69454)
- Fixed minion not honoring SIGTERM while stuck in the master DNS retry loop, which caused systemd to escalate to SIGKILL after 90 seconds. [#69466](https://github.com/saltstack/salt/issues/69466)
- Fixed ``lgpo_reg`` module and state functions failing on Windows Domain Controllers with ``Access is denied`` when writing to ``HKLM\SOFTWARE\Policies\`` subkeys. The ``set_value``, ``disable_value``, and ``delete_value`` execution module functions now accept a ``write_registry`` parameter (default ``None``) that auto-detects Domain Controllers via the ``ProductType`` registry key and skips the direct registry write when one is detected, instead relying on the Group Policy engine to apply the policy on the next refresh. An explicit ``True`` or ``False`` overrides auto-detection. A ``refresh_policy`` parameter (default ``False``) has been added to all three functions to trigger an in-process ``userenv.RefreshPolicy`` call immediately after the ``Registry.pol`` file is updated. The corresponding state functions ``value_present``, ``value_disabled``, and ``value_absent`` expose the same parameters. A standalone ``lgpo_reg.refresh_policy`` execution function and ``lgpo_reg.refresh_policy`` state have been added to allow a single Group Policy refresh to be issued after a batch of policy writes. ``is_domain_controller`` has been added to ``salt.utils.win_functions`` and ``refresh_policy`` has been added to ``salt.utils.win_lgpo_reg``. [#69468](https://github.com/saltstack/salt/issues/69468)
- Fixed 3006.x Windows nightly CI by pinning the runner-host Python to 3.14.6 (OpenSSL 3.5.7); the setup-python default `3.14` was resolving to a cached 3.14.5 build whose OpenSSL 3.0.20 rejected the cert pypi.org currently serves. [#69486](https://github.com/saltstack/salt/issues/69486)
- Fixed 3006.x Windows nightly CI Deps by dropping a sitecustomize hook into the salt onedir's `Lib/site-packages` that applies the cpython#104135 iter-and-skip patch before pip touches TLS; the prior runner-host Python pin in #69486 targeted the wrong interpreter (the failing pip runs in a venv created from the relenv-bundled Python 3.10) and is reverted. [#69490](https://github.com/saltstack/salt/issues/69490)
- Fixed ``lgpo_reg`` failures on Windows when ``Registry.pol`` is temporarily locked by the Group Policy service or other processes. Salt now uses ``EnterCriticalPolicySection`` / ``LeaveCriticalPolicySection`` from ``userenv.dll`` — the same synchronization primitive used by the GP engine — to serialize read-modify-write access to ``Registry.pol``. A retry loop with configurable attempts and delay is also applied for non-GP lockers such as antivirus scanners or VSS snapshots that do not participate in the GP critical section handshake. [#69492](https://github.com/saltstack/salt/issues/69492)


### Added

- Added ``shadow.verify_password`` to ``salt.modules.win_shadow``, which
    validates a Windows user's password via ``LogonUser`` with
    ``LOGON32_LOGON_NETWORK`` (Microsoft's recommended approach per
    `KB180548 <https://support.microsoft.com/en-us/help/180548>`_) without
    creating an interactive session. If the check causes an account lockout,
    the account is automatically unlocked. Updated ``user.present`` on Windows
    to use ``shadow.verify_password`` so the password is only changed when it
    differs from the current value, matching the idempotent behaviour on other
    platforms. [#41347](https://github.com/saltstack/salt/issues/41347)
- Added ability to configure the pillar destination for the `netbox` ext_pillar via `destination_pillar_key` [#65531](https://github.com/saltstack/salt/issues/65531)
- Migrate Salt documentation to the PyData Sphinx theme. This update modernizes the documentation UI, improves navigation with a persistent sidebar tree, and fixes issues with embedded video playback. [#69185](https://github.com/saltstack/salt/issues/69185)
- fix etcdv3 module authentification when using etcd3-py lib [#69202](https://github.com/saltstack/salt/issues/69202)
- Added ``lgpo_reg.get_rsop_value`` to query the Resultant Set of Policy (RSoP) for a registry key/value and detect whether it is managed by a Domain Group Policy Object. The ``lgpo_reg`` module functions ``set_value``, ``disable_value``, and ``delete_value`` now log a warning when a Domain GPO is detected for the target value. The ``lgpo_reg`` state functions ``value_present``, ``value_disabled``, and ``value_absent`` append the same warning to the state comment so it is visible in state output. [#69205](https://github.com/saltstack/salt/issues/69205)
