git: 8ec108914247 - main - nuageinit: adopt cloud-init disable_root semantics

From: Baptiste Daroussin <bapt_at_FreeBSD.org>
Date: Tue, 11 Aug 2026 11:17:23 UTC
The branch main has been updated by bapt:

URL: https://cgit.FreeBSD.org/src/commit/?id=8ec108914247502dff049059dc60df17920731c8

commit 8ec108914247502dff049059dc60df17920731c8
Author:     Baptiste Daroussin <bapt@FreeBSD.org>
AuthorDate: 2026-08-11 08:56:52 +0000
Commit:     Baptiste Daroussin <bapt@FreeBSD.org>
CommitDate: 2026-08-11 11:15:55 +0000

    nuageinit: adopt cloud-init disable_root semantics
    
    disable_root now restricts root's authorized_keys instead of setting
    PermitRootLogin.
    
    Reported by:    np@
---
 libexec/nuageinit/nuage.lua          |  8 ++++++--
 libexec/nuageinit/nuageinit          | 40 ++++++++++++++++++++++++++----------
 libexec/nuageinit/nuageinit.7        | 35 +++++++++++++++++++------------
 libexec/nuageinit/tests/nuageinit.sh | 31 ++++++++++++++--------------
 4 files changed, 73 insertions(+), 41 deletions(-)

diff --git a/libexec/nuageinit/nuage.lua b/libexec/nuageinit/nuage.lua
index cbd842460e55..3a9ac253fbcb 100644
--- a/libexec/nuageinit/nuage.lua
+++ b/libexec/nuageinit/nuage.lua
@@ -438,7 +438,7 @@ local function addgroup(grp)
 	return true
 end
 
-local function addsshkey(homedir, key)
+local function addsshkey(homedir, key, options)
 	local root = os.getenv("NUAGE_FAKE_ROOTDIR")
 	if root then
 		homedir = root .. "/" .. homedir
@@ -471,7 +471,11 @@ local function addsshkey(homedir, key)
 		warnmsg("impossible to open " .. ak_path)
 		return
 	end
-	f:write(key .. "\n")
+	if options and options ~= "" then
+		f:write(options .. " " .. key .. "\n")
+	else
+		f:write(key .. "\n")
+	end
 	f:close()
 
 	-- Set permissions and ownership on newly created files/dirs
diff --git a/libexec/nuageinit/nuageinit b/libexec/nuageinit/nuageinit
index ba26f504effb..d67ac6ce4229 100755
--- a/libexec/nuageinit/nuageinit
+++ b/libexec/nuageinit/nuageinit
@@ -570,18 +570,36 @@ local function ssh_deletekeys(obj)
 	end
 end
 
-local function disable_root(obj)
-	if obj.disable_root == nil then return end
-	if obj.disable_root then
-		local value = "no"
-		if obj.disable_root_opts then
-			if type(obj.disable_root_opts) == "string" then
-				value = obj.disable_root_opts
-			elseif type(obj.disable_root_opts) == "table" then
-				value = obj.disable_root_opts[1]
-			end
+local DISABLE_USER_OPTS = 'no-port-forwarding,no-agent-forwarding,no-X11-forwarding,command="echo \'Please login as the user \\"$USER\\" rather than the user \\"$DISABLE_USER\\".\';echo;sleep 10;exit 142"'
+
+local function disable_root(obj, metadata)
+	-- cloud-init semantics: restrict root's authorized_keys options
+	local disable = true
+	if obj.disable_root ~= nil then
+		disable = obj.disable_root
+	end
+	local opts = ""
+	if disable then
+		opts = obj.disable_root_opts or DISABLE_USER_OPTS
+		if type(opts) == "table" then
+			opts = opts[1]
+		end
+		opts = opts:gsub("%$USER", "freebsd")
+		opts = opts:gsub("%$DISABLE_USER", "root")
+	end
+	local keys = {}
+	if type(metadata.public_keys) == "table" then
+		for _, k in pairs(metadata.public_keys) do
+			table.insert(keys, k)
 		end
-		nuage.update_sshd_config("PermitRootLogin", value)
+	end
+	if obj and type(obj.ssh_authorized_keys) == "table" then
+		for _, k in ipairs(obj.ssh_authorized_keys) do
+			table.insert(keys, k)
+		end
+	end
+	for _, k in ipairs(keys) do
+		nuage.addsshkey("/root", k, opts)
 	end
 end
 
diff --git a/libexec/nuageinit/nuageinit.7 b/libexec/nuageinit/nuageinit.7
index a3d9da2415d9..c9c9096eee72 100644
--- a/libexec/nuageinit/nuageinit.7
+++ b/libexec/nuageinit/nuageinit.7
@@ -331,25 +331,34 @@ configuration in
 .It Ic disable_root
 Boolean which determines if root login via SSH should be disabled.
 If set to
-.Ar true ,
-sets
-.Qq Ic PermitRootLogin
-to
-.Ar no
-.Pq or the value specified in Ic disable_root_opts
-in
-.Pa /etc/ssh/sshd_config .
+.Ar true
+.Pq the default ,
+the public SSH keys are written to
+.Pa /root/.ssh/authorized_keys
+with the options specified in
+.Ic disable_root_opts ,
+which by default redirects the login to the default user.
+If set to
+.Ar false ,
+the public SSH keys are written to
+.Pa /root/.ssh/authorized_keys
+without any restriction.
 .It Ic disable_root_opts
-String or array of options used to set the value of
-.Qq Ic PermitRootLogin
-in
-.Pa /etc/ssh/sshd_config ,
+String or array of options used to prefix the public SSH keys in
+.Pa /root/.ssh/authorized_keys
 when
 .Ic disable_root
 is set to
 .Ar true .
+The
+.Ar $USER
+and
+.Ar $DISABLE_USER
+placeholders are replaced with the default user name and
+.Ar root
+respectively.
 If not specified, defaults to
-.Ar no .
+.Ar no-port-forwarding,no-agent-forwarding,no-X11-forwarding,command="echo 'Please login as the user "$USER" rather than the user "$DISABLE_USER".';echo;sleep 10;exit 142" .
 .Pp
 Only the first value is used when an array is provided.
 .It Ic network
diff --git a/libexec/nuageinit/tests/nuageinit.sh b/libexec/nuageinit/tests/nuageinit.sh
index ab9e697076e1..1fb5f7b4c967 100644
--- a/libexec/nuageinit/tests/nuageinit.sh
+++ b/libexec/nuageinit/tests/nuageinit.sh
@@ -267,6 +267,7 @@ ssh_authorized_keys:
   - "ssh-rsa AAAAB3NzaC1y...== Generated by Nova"
 EOF
 	mkdir -p etc
+	mkdir -p root
 	cat > etc/master.passwd << EOF
 root:*:0:0::0:0:Charlie &:/root:/bin/sh
 sys:*:1:0::0:0:Sys:/home/sys:/bin/sh
@@ -294,6 +295,7 @@ ssh_authorized_keys:
   - "ssh-rsa AAAAB3NzaC1y...== Generated by Nova"
 EOF
 	mkdir -p etc
+	mkdir -p root
 	cat > etc/master.passwd << EOF
 root:*:0:0::0:0:Charlie &:/root:/bin/sh
 sys:*:1:0::0:0:Sys:/home/sys:/bin/sh
@@ -340,6 +342,7 @@ config2_pubkeys_meta_data_body()
 }
 EOF
 	mkdir -p etc
+	mkdir -p root
 	cat > etc/master.passwd << EOF
 root:*:0:0::0:0:Charlie &:/root:/bin/csh
 sys:*:1:0::0:0:Sys:/home/sys:/bin/csh
@@ -1004,37 +1007,35 @@ config2_userdata_disable_root_body()
 {
 	mkdir -p media/nuageinit
 	setup_test_adduser
+	mkdir -p root
 	printf "{}" > media/nuageinit/meta_data.json
 	cat > media/nuageinit/user_data <<EOF
 #cloud-config
 disable_root: true
-EOF
-	mkdir -p etc/ssh
-	touch etc/ssh/sshd_config
-	atf_check -o empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
-	atf_check -o inline:"PermitRootLogin no\n" cat etc/ssh/sshd_config
-	cat > media/nuageinit/user_data <<EOF
-#cloud-config
-disable_root: true
-disable_root_opts: "without-password"
+ssh_authorized_keys:
+  - "ssh-rsa AAAAB3NzaC1y...== Generated by Nova"
 EOF
 	atf_check -o empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
-	atf_check -o inline:"PermitRootLogin without-password\n" cat etc/ssh/sshd_config
+	atf_check -o match:'no-port-forwarding,no-agent-forwarding,no-X11-forwarding,command=.*exit 142" ssh-rsa AAAAB3NzaC1y...== Generated by Nova' cat root/.ssh/authorized_keys
+	rm -f root/.ssh/authorized_keys
 	cat > media/nuageinit/user_data <<EOF
 #cloud-config
 disable_root: true
-disable_root_opts:
-  - "prohibit-password"
+disable_root_opts: "no-port-forwarding"
+ssh_authorized_keys:
+  - "ssh-rsa AAAAB3NzaC1y...== Generated by Nova"
 EOF
 	atf_check -o empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
-	atf_check -o inline:"PermitRootLogin prohibit-password\n" cat etc/ssh/sshd_config
+	atf_check -o inline:"no-port-forwarding ssh-rsa AAAAB3NzaC1y...== Generated by Nova\n" cat root/.ssh/authorized_keys
+	rm -f root/.ssh/authorized_keys
 	cat > media/nuageinit/user_data <<EOF
 #cloud-config
 disable_root: false
+ssh_authorized_keys:
+  - "ssh-rsa AAAAB3NzaC1y...== Generated by Nova"
 EOF
-	echo "PermitRootLogin yes" > etc/ssh/sshd_config
 	atf_check -o empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
-	atf_check -o inline:"PermitRootLogin yes\n" cat etc/ssh/sshd_config
+	atf_check -o inline:"ssh-rsa AAAAB3NzaC1y...== Generated by Nova\n" cat root/.ssh/authorized_keys
 }
 
 config2_userdata_bootcmd_head()