[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1110033: unblock: openssh/1:10.0p1-6



Package: release.debian.org
Severity: normal
X-Debbugs-Cc: openssh@packages.debian.org
Control: affects -1 + src:openssh
User: release.debian.org@packages.debian.org
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)                              [cjwatson@debian.org]
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 <cjwatson@debian.org>  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

Reply to: