Package: release.debian.org Severity: normal X-Debbugs-Cc: [email protected] Control: affects -1 + src:openssh User: [email protected] Usertags: unblock
[ Reason ] In OpenSSH 9.8, upstream split some of the responsibilities of sshd out to a new sshd-session binary, in order to reduce the attack surface of the process that listens on port 22. (Parts of sshd-session were later split out to sshd-auth as well.) When sshd receives a new connection, it used to fork and re-exec itself with special command-line parameters to handle the session. Following the split, it forks and execs sshd-session instead, and sshd no longer supports the parameters telling it to act as a session process. Since the listener process is only restarted in the postinst, this means that between the unpack and configure stages of openssh-server across this change (in particular, during an upgrade from bookworm to trixie), the listener is unable to start any new SSH sessions. This may cover a large part of the duration of the upgrade. [ Impact ] As described in https://bugs.debian.org/1109742, during most of the upgrade process from bookworm to trixie, it's impossible to initiate new SSH connections. If the upgrade fails, and the user forgets to maintain a separate SSH connection or their network connection is interrupted, the result may be a failed remote upgrade with no way to access the system. [ Tests ] I've tested this manually by creating a bookworm container and running the relevant parts of the upgrade step by step, something like this (obviously set up for me, but adjust as needed): $ incus launch images:debian/bookworm openssh-upgrade $ incus exec openssh-upgrade -- apt -y install openssh-server $ incus exec openssh-upgrade -- adduser --disabled-password --comment 'Colin Watson' cjwatson $ incus file push -p --uid 1000 --gid 1000 --mode=600 .ssh/id_ed25519.pub openssh-upgrade/home/cjwatson/.ssh/authorized_keys Then run "while :; do date -Ins; ssh openssh-upgrade.incus true; sleep 0.1; done" in a separate terminal to monitor connectivity, and continue the upgrade with: $ dcmd incus file push openssh_10.0p1-6_amd64.changes openssh-upgrade/root/ $ incus exec openssh-upgrade -- dpkg --unpack openssh-{client,server,sftp-server}_10.0p1-6_amd64.deb $ incus exec openssh-upgrade -- sed -i 's/bookworm/trixie/' /etc/apt/sources.list $ incus exec openssh-upgrade -- apt update $ incus exec openssh-upgrade -- apt -f install [ Risks ] My first approach was to patch the new sshd to notice when it's been invoked with -R and exec sshd-session instead, but that turned out not to work reliably: it's possible for the new sshd to fail at the dynamic linker stage immediately after openssh-server has been unpacked, e.g. because it needs a newer libc6. The self-diversion approach is a bit alarming, but it limits the scope of the workaround code to just the affected upgrade scenarios, and the code is mechanically simple enough even if it requires some careful thinking. I can't think of any better approaches. [ Checklist ] [x] all changes are documented in the d/changelog [x] I reviewed all changes and I approve them [x] attach debdiff against the package in testing [ Other info ] Connections during upgrade still fail between libssl3 being replaced by libssl3t64 at a newer version and the new openssh-server being configured. While that's a similar symptom, it's a separate problem that will need a stable update to bookworm; that's covered by https://bugs.debian.org/1110030. unblock openssh/1:10.0p1-6 -- Colin Watson (he/him) [[email protected]]
diff --git a/debian/changelog b/debian/changelog index 8aa7ac0aa..8fedadf2f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +openssh (1:10.0p1-6) unstable; urgency=medium + + * Temporarily divert /usr/sbin/sshd during upgrades from before + 1:9.8p1-1~, to avoid new connections failing between unpack and + configure (closes: #1109742). + + -- Colin Watson <[email protected]> Mon, 28 Jul 2025 12:17:42 +0100 + openssh (1:10.0p1-5) unstable; urgency=medium * Ensure that configure knows the path to passwd; fixes reproducibility of diff --git a/debian/openssh-server.postinst b/debian/openssh-server.postinst index 7e4a62c65..c0d43006d 100644 --- a/debian/openssh-server.postinst +++ b/debian/openssh-server.postinst @@ -11,10 +11,16 @@ umask 022 get_config_option() { option="$1" + sshd_path=/usr/sbin/sshd [ -f /etc/ssh/sshd_config ] || return - /usr/sbin/sshd -G | sed -n "s/^$option //Ip" + # begin-remove-after: released:forky + if [ -e /usr/sbin/sshd.session-split ]; then + sshd_path=/usr/sbin/sshd.session-split + fi + # end-remove-after + "$sshd_path" -G | sed -n "s/^$option //Ip" } @@ -109,6 +115,24 @@ if [ "$action" = configure ]; then systemctl unmask ssh.service systemctl disable ssh.service fi + # begin-remove-after: released:forky + if dpkg --compare-versions "$2" lt-nl 1:9.8p1-1~; then + # We're ready to restart the listener process so that it + # executes sshd-session rather than sshd for new + # connections, so we can remove this diversion now. This + # starts a brief window where new connections will fail + # (ending when the service is restarted), but at least it's + # all contained within this postinst. + # + # See openssh-server.preinst for why we use this odd package + # name. + dpkg-divert --package openssh-client --remove --no-rename \ + --divert /usr/sbin/sshd.session-split /usr/sbin/sshd + if [ -e /usr/sbin/sshd.session-split ]; then + mv -f /usr/sbin/sshd.session-split /usr/sbin/sshd + fi + fi + # end-remove-after fi #DEBHELPER# diff --git a/debian/openssh-server.preinst b/debian/openssh-server.preinst new file mode 100755 index 000000000..e6eff9050 --- /dev/null +++ b/debian/openssh-server.preinst @@ -0,0 +1,21 @@ +#!/bin/sh +set -e + +# begin-remove-after: released:forky +if [ "$1" = upgrade ] && dpkg --compare-versions "$2" lt-nl 1:9.8p1-1~; then + # Temporarily divert the new sshd binary, since OpenSSH 9.8 moved + # part of its responsibilities to sshd-session, and unpacking the + # new sshd to its normal path would break new connections. We'll + # remove this diversion when we're ready to restart the listener + # process. + # + # Since we're trying to divert a file shipped in this package, we + # use a package name that we know doesn't ship /usr/sbin/sshd. + dpkg-divert --package openssh-client --add --no-rename \ + --divert /usr/sbin/sshd.session-split /usr/sbin/sshd +fi +# end-remove-after + +#DEBHELPER# + +exit 0

