diff options
author | A. Wilcox <AWilcox@Wilcox-Tech.com> | 2023-12-01 21:39:58 -0600 |
---|---|---|
committer | A. Wilcox <AWilcox@Wilcox-Tech.com> | 2023-12-01 21:47:21 -0600 |
commit | 36dc1b7a2ea44321a2c233fd7ea576ed516d4ccc (patch) | |
tree | 323d215d395f5e7b276f7fe267d9952e884cce69 | |
parent | 8e5abd31f932c51a1f153e02d769eaebd6a2a543 (diff) | |
download | horizon-36dc1b7a2ea44321a2c233fd7ea576ed516d4ccc.tar.gz horizon-36dc1b7a2ea44321a2c233fd7ea576ed516d4ccc.tar.bz2 horizon-36dc1b7a2ea44321a2c233fd7ea576ed516d4ccc.tar.xz horizon-36dc1b7a2ea44321a2c233fd7ea576ed516d4ccc.zip |
Add 'rootshell' key to determine root's shell
* Key added to code and documentation.
* Tests added and pass locally on gwyn (ppc64) and fran (aarch64).
* Qt UI automatically sets /bin/zsh as root's shell (ref: packages#206).
* ISO image creator backend no longer has root shell hack.
-rw-r--r-- | devel/requirements/3b_runner.xml | 16 | ||||
-rw-r--r-- | devel/script/2_keys.xml | 31 | ||||
-rw-r--r-- | hscript/meta.cc | 36 | ||||
-rw-r--r-- | hscript/meta.hh | 11 | ||||
-rw-r--r-- | hscript/script.cc | 3 | ||||
-rw-r--r-- | hscript/script_e.cc | 12 | ||||
-rw-r--r-- | hscript/script_i.hh | 13 | ||||
-rw-r--r-- | image/backends/iso.cc | 5 | ||||
-rw-r--r-- | tests/fixtures/0265-rootshell-basic.installfile | 6 | ||||
-rw-r--r-- | tests/fixtures/0266-rootshell-invalid.installfile | 6 | ||||
-rw-r--r-- | tests/fixtures/0267-rootshell-duplicate.installfile | 7 | ||||
-rw-r--r-- | tests/spec/validator_spec.rb | 18 | ||||
-rw-r--r-- | ui/qt5/horizonwizard.cc | 2 |
13 files changed, 159 insertions, 7 deletions
diff --git a/devel/requirements/3b_runner.xml b/devel/requirements/3b_runner.xml index b5e9848..9ab06bd 100644 --- a/devel/requirements/3b_runner.xml +++ b/devel/requirements/3b_runner.xml @@ -378,6 +378,14 @@ <title>Runner.Validate.usergroups.Group</title> <para>The system shall verify that each group specified is a valid system-defined group name that is present in the base system image <filename>/etc/group</filename> file.</para> </formalpara> + <formalpara id="Runner.Validate.rootshell"> + <title>Runner.Validate.rootshell</title> + <para>The system shall verify that the HorizonScript contains zero or one <literal>rootshell</literal> key.</para> + </formalpara> + <formalpara id="Runner.Validate.rootshell.Format"> + <title>Runner.Validate.rootshell.Format</title> + <para>The system shall verify that the value of the <literal>rootshell</literal> key, if present, begins with a <literal>/</literal>.</para> + </formalpara> <formalpara id="Runner.Validate.diskid"> <title>Runner.Validate.diskid</title> <para>The system shall verify any <literal>diskid</literal> keys contained in the HorizonScript.</para> @@ -828,6 +836,14 @@ <title>Runner.Execute.svcenable.AddlRunlevels</title> <para>If a runlevel not defined by the system service manager is specified in a <literal>svcenable</literal> key, it shall be interpreted to be a stacked runlevel atop the default runlevel and shall be configured as such in the target namespace.</para> </formalpara> + <formalpara id="Runner.Execute.rootshell"> + <title>Runner.Execute.rootshell</title> + <para>If a <literal>rootshell</literal> key is specified in the HorizonScript, the system shall set the shell of the <literal>root</literal> user to the value specified.</para> + </formalpara> + <formalpara id="Runner.Execute.rootshell.Exists"> + <title>Runner.Execute.rootshell.Exists</title> + <para>If the <literal>rootshell</literal> key specifies a file that does not exist or is not executable, the system shall set the shell of the <literal>root</literal> user to the value<literal>/bin/sh</literal>.</para> + </formalpara> <formalpara id="Runner.Execute.bootloader"> <title>Runner.Execute.bootloader</title> <para>If a <literal>bootloader</literal> key is specified in the HorizonScript, the system shall perform the requested bootloader configuration.</para> diff --git a/devel/script/2_keys.xml b/devel/script/2_keys.xml index bd8469c..854f0d5 100644 --- a/devel/script/2_keys.xml +++ b/devel/script/2_keys.xml @@ -695,6 +695,37 @@ signingkey https://packages/builder@ourcompany.net.pub </para> </formalpara> </section> + <section id="rootshell"> + <title><literal>rootshell</literal></title> + <formalpara id="rootshell.name"> + <title>Name</title> + <para><literal>rootshell</literal></para> + </formalpara> + <formalpara id="rootshell.purpose"> + <title>Purpose</title> + <para>The <literal>rootshell</literal> key specifies the shell to use when logging in to the target computer as the <literal>root</literal> user.</para> + </formalpara> + <formalpara id="rootshell.format"> + <title>Format</title> + <para>The <literal>rootshell</literal> key is a single string value containing the full path inside the target to the executable to use as the shell.</para> + </formalpara> + <formalpara id="rootshell.default"> + <title>Default</title> + <para>If no <literal>rootshell</literal> key is specified, the target computer will use <literal>/bin/sh</literal> as the shell when logging in as the <literal>root</literal> user.</para> + </formalpara> + <formalpara id="rootshell.example"> + <title>Example</title> + <para> + <example> + <title>The <literal>rootshell</literal> Key</title> + <programlisting> + rootshell /bin/zsh + </programlisting> + <para>This will configure the target computer to use <literal>/bin/zsh</literal> as the shell when logging in as the <literal>root</literal> user.</para> + </example> + </para> + </formalpara> + </section> <section id="bootloader"> <title><literal>bootloader</literal></title> <formalpara id="bootloader.name"> diff --git a/hscript/meta.cc b/hscript/meta.cc index ee34372..8079b1a 100644 --- a/hscript/meta.cc +++ b/hscript/meta.cc @@ -708,6 +708,42 @@ bool Version::execute() const { /* LCOV_EXCL_STOP */ +Key* RootShell::parseFromData(const std::string &data, + const ScriptLocation &pos, int *errors, int *, + const Script *script) { + if(data.at(0) != '/') { + if(errors) *errors += 1; + output_error(pos, "rootshell: invalid shell specified", data); + return nullptr; + } + + return new RootShell(script, pos, data); +} + +bool RootShell::execute() const { + auto target = script->targetDirectory(); + + if(script->options().test(Simulate)) { + std::cout << "[ -x " << target << _value << "] && " + << "sed -i 's#/root:/bin/sh$#/root:" << _value << "#' " + << target << "/etc/passwd" << std::endl; + } +#ifdef HAS_INSTALL_ENV + else { + if(fs::exists(target + _value)) { + run_command("sed", {"-i", "s#/root:/bin/sh$#/root:" + _value + "#", + script->targetDirectory() + "/etc/passwd"}); + } else { + output_warning(pos, "shell " + _value + + " not found; leaving root shell as /bin/sh"); + } + } +#endif /* HAS_INSTALL_ENV */ + + return true; +} + + const std::string my_arch(const Horizon::Script *script) { const Key *arch_key = script->getOneValue("arch"); if(arch_key != nullptr) { diff --git a/hscript/meta.hh b/hscript/meta.hh index 32f8eb7..3d1382e 100644 --- a/hscript/meta.hh +++ b/hscript/meta.hh @@ -146,6 +146,17 @@ public: bool execute() const override; }; +class RootShell : public StringKey { +private: + RootShell(const Script *_s, const ScriptLocation &_p, + const std::string &_v) : + StringKey(_s, _p, _v) {} +public: + static Key *parseFromData(const std::string &, const ScriptLocation &, + int *, int *, const Script *); + bool execute() const override; +}; + class Bootloader : public Key { private: const std::string _device; diff --git a/hscript/script.cc b/hscript/script.cc index d86216d..0126a99 100644 --- a/hscript/script.cc +++ b/hscript/script.cc @@ -52,6 +52,7 @@ const std::map<std::string, key_parse_fn> valid_keys = { {"signingkey", &SigningKey::parseFromData}, {"svcenable", &SvcEnable::parseFromData}, {"version", &Version::parseFromData}, + {"rootshell", &RootShell::parseFromData}, {"bootloader", &Bootloader::parseFromData}, {"netconfigtype", &NetConfigType::parseFromData}, @@ -129,6 +130,8 @@ bool Script::ScriptPrivate::store_key(const std::string &key_name, Key *obj, return store_svcenable(obj, pos, errors, warnings, opts); } else if(key_name == "version") { return store_version(obj, pos, errors, warnings, opts); + } else if(key_name == "rootshell") { + return store_rootshell(obj, pos, errors, warnings, opts); } else if(key_name == "bootloader") { return store_bootloader(obj, pos, errors, warnings, opts); } else if(key_name == "username") { diff --git a/hscript/script_e.cc b/hscript/script_e.cc index 50f92e1..738bfed 100644 --- a/hscript/script_e.cc +++ b/hscript/script_e.cc @@ -647,8 +647,12 @@ bool Script::execute() const { if(internal->keymap) { EXECUTE_OR_FAIL("keymap", internal->keymap) - fs::create_symlink("/etc/init.d/keymaps", - targ_etc + "/runlevels/default/keymaps", ec); +#ifdef HAS_INSTALL_ENV + if(!opts.test(Simulate)) { + fs::create_symlink("/etc/init.d/keymaps", + targ_etc + "/runlevels/default/keymaps", ec); + } +#endif /* HAS_INSTALL_ENV */ } for(auto &acct : internal->accounts) { @@ -697,6 +701,10 @@ bool Script::execute() const { } #endif + if(internal->root_shell) { + EXECUTE_OR_FAIL("rootshell", internal->root_shell) + } + if(internal->boot) { EXECUTE_OR_FAIL("bootloader", internal->boot) } diff --git a/hscript/script_i.hh b/hscript/script_i.hh index 12ec133..641c749 100644 --- a/hscript/script_i.hh +++ b/hscript/script_i.hh @@ -65,6 +65,8 @@ struct Script::ScriptPrivate { std::unique_ptr<Version> version; /*! The desired bootloader configuration. */ std::unique_ptr<Bootloader> boot; + /*! The desired root shell. */ + std::unique_ptr<RootShell> root_shell; /*! Network addressing configuration */ std::vector< std::unique_ptr<NetAddress> > addresses; @@ -297,6 +299,17 @@ struct Script::ScriptPrivate { return true; } + bool store_rootshell(Key *obj, const ScriptLocation &pos, int *errors, + int *, const ScriptOptions &) { + if(root_shell) { + DUPLICATE_ERROR(root_shell, "rootshell", root_shell->value()) + return false; + } + std::unique_ptr<RootShell> r(dynamic_cast<RootShell *>(obj)); + root_shell = std::move(r); + return true; + } + bool store_bootloader(Key *obj, const ScriptLocation &pos, int *errors, int *, const ScriptOptions &) { if(boot) { diff --git a/image/backends/iso.cc b/image/backends/iso.cc index 8ef4503..52f0367 100644 --- a/image/backends/iso.cc +++ b/image/backends/iso.cc @@ -303,11 +303,6 @@ public: output_info("CD backend", "creating live environment /etc/fstab"); if(!write_fstab_to(target)) return FS_ERROR; - /* REQ: ISO.13 */ - output_info("CD backend", "setting root shell"); - run_command("sed", {"-i", "s#/root:/bin/sh$#/root:/bin/zsh#", - target + "/etc/passwd"}); - /* REQ: ISO.15 */ output_info("CD backend", "configuring login services"); run_command("sed", {"-i", "s/pam_unix.so$/pam_unix.so nullok_secure/", diff --git a/tests/fixtures/0265-rootshell-basic.installfile b/tests/fixtures/0265-rootshell-basic.installfile new file mode 100644 index 0000000..e23477b --- /dev/null +++ b/tests/fixtures/0265-rootshell-basic.installfile @@ -0,0 +1,6 @@ +network false +hostname 123lonelycorgi.street +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +rootshell /bin/zsh diff --git a/tests/fixtures/0266-rootshell-invalid.installfile b/tests/fixtures/0266-rootshell-invalid.installfile new file mode 100644 index 0000000..012fc67 --- /dev/null +++ b/tests/fixtures/0266-rootshell-invalid.installfile @@ -0,0 +1,6 @@ +network false +hostname 123lonelycorgi.street +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +rootshell lol hi! diff --git a/tests/fixtures/0267-rootshell-duplicate.installfile b/tests/fixtures/0267-rootshell-duplicate.installfile new file mode 100644 index 0000000..3cd25c7 --- /dev/null +++ b/tests/fixtures/0267-rootshell-duplicate.installfile @@ -0,0 +1,7 @@ +network false +hostname 123lonelycorgi.street +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +rootshell /bin/zsh +rootshell /bin/dash diff --git a/tests/spec/validator_spec.rb b/tests/spec/validator_spec.rb index 3a491a0..8f33330 100644 --- a/tests/spec/validator_spec.rb +++ b/tests/spec/validator_spec.rb @@ -732,6 +732,19 @@ RSpec.describe 'HorizonScript validation', :type => :aruba do expect(last_command_started).to have_output(/error: .*version.*/) end end + context "for 'rootshell' key" do + it "succeeds with zsh" do + use_fixture '0265-rootshell-basic.installfile' + run_validate + expect(last_command_started).to have_output(PARSER_SUCCESS) + expect(last_command_started).to have_output(VALIDATOR_SUCCESS) + end + it "fails with an invalid shell" do + use_fixture '0266-rootshell-invalid.installfile' + run_validate + expect(last_command_started).to have_output(/error: .*rootshell.*/) + end + end context "for 'pkginstall' key" do it "warns when a package is listed twice in the same line" do use_fixture '0216-pkginstall-dup-single.installfile' @@ -1284,6 +1297,11 @@ RSpec.describe 'HorizonScript validation', :type => :aruba do run_validate expect(last_command_started).to have_output(/error: .*duplicate.*timezone/) end + it "fails with a duplicate 'rootshell' key" do + use_fixture '0267-rootshell-duplicate.installfile' + run_validate + expect(last_command_started).to have_output(/error: .*duplicate.*rootshell/) + end end context "user account keys:" do context "'username'" do diff --git a/ui/qt5/horizonwizard.cc b/ui/qt5/horizonwizard.cc index 53331ff..36746ea 100644 --- a/ui/qt5/horizonwizard.cc +++ b/ui/qt5/horizonwizard.cc @@ -702,6 +702,8 @@ QString HorizonWizard::toHScript() { /* handwavy-future: When we have language support, set it here */ lines << "language en_US.UTF-8"; + lines << "rootshell /bin/zsh"; + auto iterator = valid_keymaps.begin(); std::advance(iterator, field("keymap").toInt()); lines << ("keymap " + QString::fromStdString(*iterator)); |