Date: Wed, 30 Apr 2025 07:32:25 GMT From: Baptiste Daroussin <bapt@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: c201a1198ad7 - main - nuageinit: implement chpasswd Message-ID: <202504300732.53U7WPP9087838@gitrepo.freebsd.org>
next in thread | raw e-mail | index | archive | help
The branch main has been updated by bapt: URL: https://cgit.FreeBSD.org/src/commit/?id=c201a1198ad70e7d096ee32c364d539eed2dfec4 commit c201a1198ad70e7d096ee32c364d539eed2dfec4 Author: Baptiste Daroussin <bapt@FreeBSD.org> AuthorDate: 2025-04-25 15:16:22 +0000 Commit: Baptiste Daroussin <bapt@FreeBSD.org> CommitDate: 2025-04-30 07:32:06 +0000 nuageinit: implement chpasswd Add support for chpasswd, with all possible syntaxes, including deprecated one: chpasswd.list as a list or as a multiline string as some providers are still only providing this deprecated form Sponsored by: OVHCloud MFC After: 1 week Reviewed by: kevans, jlduran Differential Revision: https://reviews.freebsd.org/D50021 --- libexec/nuageinit/nuage.lua | 105 ++++++++++++++++++++- libexec/nuageinit/nuageinit | 6 +- libexec/nuageinit/tests/nuageinit.sh | 175 +++++++++++++++++++++++++++++++++++ 3 files changed, 283 insertions(+), 3 deletions(-) diff --git a/libexec/nuageinit/nuage.lua b/libexec/nuageinit/nuage.lua index e58069164130..15af5afbd9f0 100644 --- a/libexec/nuageinit/nuage.lua +++ b/libexec/nuageinit/nuage.lua @@ -1,7 +1,7 @@ --- -- SPDX-License-Identifier: BSD-2-Clause -- --- Copyright(c) 2022 Baptiste Daroussin <bapt@FreeBSD.org> +-- Copyright(c) 2022-2025 Baptiste Daroussin <bapt@FreeBSD.org> local unistd = require("posix.unistd") local sys_stat = require("posix.sys.stat") @@ -261,6 +261,106 @@ local function update_sshd_config(key, value) os.rename(sshd_config .. ".nuageinit", sshd_config) end +local function exec_change_password(user, password, type, expire) + local root = os.getenv("NUAGE_FAKE_ROOTDIR") + local cmd = "pw " + if root then + cmd = cmd .. "-R " .. root .. " " + end + local postcmd = " -H 0" + local input = password + if type ~= nil and type == "text" then + postcmd = " -h 0" + else + if password == "RANDOM" then + input = nil + postcmd = " -w random" + end + end + cmd = cmd .. "usermod " .. user .. postcmd + if expire then + cmd = cmd .. " -p 1" + else + cmd = cmd .. " -p 0" + end + local f = io.popen(cmd .. " >/dev/null", "w") + if input then + f:write(input) + end + -- ignore stdout to avoid printing the password in case of random password + local r = f:close(cmd) + if not r then + warnmsg("fail to change user password ".. user) + warnmsg(cmd) + end +end + +local function change_password_from_line(line, expire) + local user, password = line:match("%s*(%w+):(%S+)%s*") + local type = nil + if user and password then + if password == "R" then + password = "RANDOM" + end + if not password:match("^%$%d+%$%w+%$") then + if password ~= "RANDOM" then + type = "text" + end + end + exec_change_password(user, password, type, expire) + end +end + +local function chpasswd(obj) + if type(obj) ~= "table" then + warnmsg("Invalid chpasswd entry, expecting an object") + return + end + local expire = false + if obj.expire ~= nil then + if type(obj.expire) == "boolean" then + expire = obj.expire + else + warnmsg("Invalid type for chpasswd.expire, expecting a boolean, got a ".. type(obj.expire)) + end + end + if obj.users ~= nil then + if type(obj.users) ~= "table" then + warnmsg("Invalid type for chpasswd.users, expecting a list, got a ".. type(obj.users)) + goto list + end + for _, u in ipairs(obj.users) do + if type(u) ~= "table" then + warnmsg("Invalid chpasswd.users entry, expecting an object, got a " .. type(u)) + goto next + end + if not u.name then + warnmsg("Invalid entry for chpasswd.users: missing 'name'") + goto next + end + if not u.password then + warnmsg("Invalid entry for chpasswd.users: missing 'password'") + goto next + end + exec_change_password(u.name, u.password, u.type, expire) + ::next:: + end + end + ::list:: + if obj.list ~= nil then + warnmsg("chpasswd.list is deprecated consider using chpasswd.users") + if type(obj.list) == "string" then + for line in obj.list:gmatch("[^\n]+") do + change_password_from_line(line, expire) + end + elseif type(obj.list) == "table" then + for _, u in ipairs(obj.list) do + change_password_from_line(u, expire) + end + end + end +end + local n = { warn = warnmsg, err = errmsg, @@ -270,7 +370,8 @@ local n = { adduser = adduser, addgroup = addgroup, addsshkey = addsshkey, - update_sshd_config = update_sshd_config + update_sshd_config = update_sshd_config, + chpasswd = chpasswd } return n diff --git a/libexec/nuageinit/nuageinit b/libexec/nuageinit/nuageinit index 341330e68128..74a75c88098a 100755 --- a/libexec/nuageinit/nuageinit +++ b/libexec/nuageinit/nuageinit @@ -2,7 +2,7 @@ --- -- SPDX-License-Identifier: BSD-2-Clause-FreeBSD -- --- Copyright(c) 2022 Baptiste Daroussin <bapt@FreeBSD.org> +-- Copyright(c) 2022-2025 Baptiste Daroussin <bapt@FreeBSD.org> local nuage = require("nuage") local ucl = require("ucl") @@ -359,6 +359,10 @@ if line == "#cloud-config" then end nuage.update_sshd_config("PasswordAuthentication", value) end + if obj.chpasswd ~= nil then + nuage.chpasswd(obj.chpasswd) + end + else local res, err = os.execute(path .. "/" .. ud) if not res then diff --git a/libexec/nuageinit/tests/nuageinit.sh b/libexec/nuageinit/tests/nuageinit.sh index d3b1d5e6df2e..1b67468971a6 100644 --- a/libexec/nuageinit/tests/nuageinit.sh +++ b/libexec/nuageinit/tests/nuageinit.sh @@ -20,6 +20,9 @@ atf_test_case config2_network atf_test_case config2_network_static_v4 atf_test_case config2_ssh_keys atf_test_case nocloud_userdata_cloudconfig_ssh_pwauth +atf_test_case nocloud_userdata_cloudconfig_chpasswd +atf_test_case nocloud_userdata_cloudconfig_chpasswd_list_string +atf_test_case nocloud_userdata_cloudconfig_chpasswd_list_list args_body() { @@ -512,6 +515,175 @@ EOF atf_check -o inline:"PasswordAuthentication no\n" cat etc/ssh/sshd_config } +nocloud_userdata_cloudconfig_chpasswd_head() +{ + atf_set "require.user" root +} +nocloud_userdata_cloudconfig_chpasswd_body() +{ + mkdir -p etc + cat > etc/master.passwd << EOF +root:*:0:0::0:0:Charlie &:/root:/bin/sh +sys:*:1:0::0:0:Sys:/home/sys:/bin/sh +user:*:1:0::0:0:Sys:/home/sys:/bin/sh +EOF + pwd_mkdb -d etc "${PWD}"/etc/master.passwd + cat > etc/group << EOF +wheel:*:0:root +users:*:1: +EOF + mkdir -p media/nuageinit + printf "instance-id: iid-local01\n" > "${PWD}"/media/nuageinit/meta-data + cat > media/nuageinit/user-data << 'EOF' +#cloud-config +chpasswd: + expire: true + users: + - { user: "sys", password: RANDOM } +EOF + + atf_check -o empty -e inline:"nuageinit: Invalid entry for chpasswd.users: missing 'name'\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud + # nothing modified + atf_check -o inline:"sys:*:1:0::0:0:Sys:/home/sys:/bin/sh\n" pw -R $(pwd) usershow sys + + cat > media/nuageinit/user-data << 'EOF' +#cloud-config +chpasswd: + expire: true + users: + - { name: "sys", pwd: RANDOM } +EOF + atf_check -o empty -e inline:"nuageinit: Invalid entry for chpasswd.users: missing 'password'\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud + # nothing modified + atf_check -o inline:"sys:*:1:0::0:0:Sys:/home/sys:/bin/sh\n" pw -R $(pwd) usershow sys + + cat > media/nuageinit/user-data << 'EOF' +#cloud-config +chpasswd: + expire: false + users: + - { name: "sys", password: RANDOM } +EOF + # not empty because the password is printed to stdout + atf_check -o empty -e empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud + atf_check -o match:'sys:\$.*:1:0::0:0:Sys:/home/sys:/bin/sh$' pw -R $(pwd) usershow sys + + cat > media/nuageinit/user-data << 'EOF' +#cloud-config +chpasswd: + expire: true + users: + - { name: "sys", password: RANDOM } +EOF + # not empty because the password is printed to stdout + atf_check -o empty -e empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud + atf_check -o match:'sys:\$.*:1:0::1:0:Sys:/home/sys:/bin/sh$' pw -R $(pwd) usershow sys + + cat > media/nuageinit/user-data << 'EOF' +#cloud-config +chpasswd: + expire: true + users: + - { name: "user", password: "$6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYeAHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/" } +EOF + # not empty because the password is printed to stdout + atf_check -o empty -e empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud + atf_check -o inline:'user:$6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYeAHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/:1:0::1:0:Sys:/home/sys:/bin/sh\n' pw -R $(pwd) usershow user +} + + +nocloud_userdata_cloudconfig_chpasswd_list_string_head() +{ + atf_set "require.user" root +} +nocloud_userdata_cloudconfig_chpasswd_list_string_body() +{ + mkdir -p etc + cat > etc/master.passwd << EOF +root:*:0:0::0:0:Charlie &:/root:/bin/sh +sys:*:1:0::0:0:Sys:/home/sys:/bin/sh +user:*:1:0::0:0:Sys:/home/sys:/bin/sh +EOF + pwd_mkdb -d etc "${PWD}"/etc/master.passwd + cat > etc/group << EOF +wheel:*:0:root +users:*:1: +EOF + mkdir -p media/nuageinit + printf "instance-id: iid-local01\n" > "${PWD}"/media/nuageinit/meta-data + cat > media/nuageinit/user-data << 'EOF' +#cloud-config +chpasswd: + expire: true + list: | + sys:RANDOM +EOF + + atf_check -o empty -e inline:"nuageinit: chpasswd.list is deprecated consider using chpasswd.users\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud + atf_check -o match:'sys:\$.*:1:0::1:0:Sys:/home/sys:/bin/sh$' pw -R $(pwd) usershow sys + + cat > media/nuageinit/user-data << 'EOF' +#cloud-config +chpasswd: + expire: false + list: | + sys:plop + user:$6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYeAHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/ + root:R +EOF + + atf_check -o empty -e ignore /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud + atf_check -o match:'sys:\$.*:1:0::0:0:Sys:/home/sys:/bin/sh$' pw -R $(pwd) usershow sys + atf_check -o inline:'user:$6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYeAHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/:1:0::0:0:Sys:/home/sys:/bin/sh\n' pw -R $(pwd) usershow user + atf_check -o match:'root:\$.*:0:0::0:0:Charlie &:/root:/bin/sh$' pw -R $(pwd) usershow root +} + +nocloud_userdata_cloudconfig_chpasswd_list_list_head() +{ + atf_set "require.user" root +} +nocloud_userdata_cloudconfig_chpasswd_list_list_body() +{ + mkdir -p etc + cat > etc/master.passwd << EOF +root:*:0:0::0:0:Charlie &:/root:/bin/sh +sys:*:1:0::0:0:Sys:/home/sys:/bin/sh +user:*:1:0::0:0:Sys:/home/sys:/bin/sh +EOF + pwd_mkdb -d etc "${PWD}"/etc/master.passwd + cat > etc/group << EOF +wheel:*:0:root +users:*:1: +EOF + mkdir -p media/nuageinit + printf "instance-id: iid-local01\n" > "${PWD}"/media/nuageinit/meta-data + cat > media/nuageinit/user-data << 'EOF' +#cloud-config +chpasswd: + expire: true + list: + - sys:RANDOM +EOF + + atf_check -o empty -e inline:"nuageinit: chpasswd.list is deprecated consider using chpasswd.users\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud + atf_check -o match:'sys:\$.*:1:0::1:0:Sys:/home/sys:/bin/sh$' pw -R $(pwd) usershow sys + + cat > media/nuageinit/user-data << 'EOF' +#cloud-config +chpasswd: + expire: false + list: + - sys:plop + - user:$6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYeAHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/ + - root:R +EOF + + atf_check -o empty -e ignore /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud + atf_check -o match:'sys:\$.*:1:0::0:0:Sys:/home/sys:/bin/sh$' pw -R $(pwd) usershow sys + atf_check -o inline:'user:$6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYeAHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/:1:0::0:0:Sys:/home/sys:/bin/sh\n' pw -R $(pwd) usershow user + atf_check -o match:'root:\$.*:0:0::0:0:Charlie &:/root:/bin/sh$' pw -R $(pwd) usershow root +} + atf_init_test_cases() { atf_add_test_case args @@ -528,4 +700,7 @@ atf_init_test_cases() atf_add_test_case config2_network_static_v4 atf_add_test_case config2_ssh_keys atf_add_test_case nocloud_userdata_cloudconfig_ssh_pwauth + atf_add_test_case nocloud_userdata_cloudconfig_chpasswd + atf_add_test_case nocloud_userdata_cloudconfig_chpasswd_list_string + atf_add_test_case nocloud_userdata_cloudconfig_chpasswd_list_list }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202504300732.53U7WPP9087838>