Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ rule names the box's actual login user instead of a hardcoded `lagerdata`.
diagnostics instead of dying later at the container step.
- **Box-config passwordless sudo works on boxes whose login user isn't `lagerdata`.**
The `/etc/sudoers.d/lager-box-config` rule written by `lager install`/`lager update`
hardcoded the `lagerdata` username, so on boxes with a different login user (e.g.
`juultest`) the grant never matched — install ended with "Sudoers file installed
hardcoded the `lagerdata` username, so on boxes with a different login user the
grant never matched — install ended with "Sudoers file installed
but `sudo -n apt-get` still fails" and `lager box config apply` required manual
setup. The rule now names the box's actual login user (validated before being
interpolated into sudoers content), already-provisioned wrong-user boxes re-bootstrap
Expand Down
4 changes: 2 additions & 2 deletions cli/commands/box/_host_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@
# `lager install` and `lager update` write this rule to BOXCFG_SUDOERS_PATH
# so `lager box config apply` can run apt-get/sysctl/mkdir/chown over
# BatchMode SSH. The rule must name the actual login user: it used to
# hardcode `lagerdata`, so on boxes with a different user (e.g. the juultest
# fleet) the grant never matched — install ended with "Sudoers file installed
# hardcode `lagerdata`, so on boxes with a different login user the grant
# never matched — install ended with "Sudoers file installed
# but `sudo -n apt-get` still fails" and every apply needed manual setup.
#
# The username lands inside a root-owned sudoers file, so callers must gate
Expand Down
2 changes: 1 addition & 1 deletion cli/commands/box/dut.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
def _bench_sudoers_bootstrap(user: str = "lagerdata") -> str:
"""Manual-fix text for a box missing the bench.json sudo grant. Names
the box's actual login user so the pasted rule matches it (a hardcoded
`lagerdata` never matched on e.g. juultest boxes)."""
`lagerdata` never matched on boxes with a different login user)."""
# Error-path text renderer: never raise, never interpolate a non-plain
# username (it comes from local box storage unvalidated) into a
# paste-into-root-shell snippet. Same rule as _host_ops.sudoers_bootstrap.
Expand Down
7 changes: 3 additions & 4 deletions cli/commands/utility/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,12 +465,11 @@ def install(ctx, box, ip, user, version, skip_jlink, skip_firewall, skip_verify,
# rule must grant NOPASSWD up front. The rule content lives in
# _host_ops.boxcfg_sudoers_rules — it must name the actual login user
# (it previously hardcoded `lagerdata`, so on boxes with a different
# user, e.g. the juultest fleet, the grant never matched and the verify
# below always warned "sudo -n apt-get still fails").
# login user the grant never matched and the verify below always
# warned "sudo -n apt-get still fails").
#
# Idempotent: re-running install overwrites the file with the same
# content. Failure here is a warning, not fatal — the box is otherwise
# installed; the operator can apply the rule manually later.
# content. Failure here is a warning, not fatal — the box is otherwise # installed; the operator can apply the rule manually later.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The comment line was accidentally joined with the next line, resulting in a nested comment marker (#) and poor formatting. Please split it back into two lines for readability.

Suggested change
# content. Failure here is a warning, not fatal — the box is otherwise # installed; the operator can apply the rule manually later.
# content. Failure here is a warning, not fatal — the box is otherwise
# installed; the operator can apply the rule manually later.

click.echo()
click.secho("Configuring passwordless sudo for `lager box config apply`...", fg='cyan')
click.echo("(One-time setup. You'll be prompted for the sudo password on the box.)")
Expand Down
4 changes: 2 additions & 2 deletions cli/commands/utility/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -1449,8 +1449,8 @@ def _render_sudoers(ok):
# where sudo can't prompt. The rule (built in
# _host_ops.boxcfg_sudoers_bootstrap_cmd) grants narrow NOPASSWD for
# exactly those operations, and must name the actual login user — it
# previously hardcoded `lagerdata`, so on boxes with a different user
# (e.g. the juultest fleet) the grant never matched. The probe ran the
# previously hardcoded `lagerdata`, so on boxes with a different login
# user the grant never matched. The probe ran the
# functional check (marker file present + `sudo -n apt-get` actually
# works) as that user, so wrong-user boxes come back negative and
# re-bootstrap here with the corrected rule. Runs on every update so
Expand Down
6 changes: 3 additions & 3 deletions test/unit/box/test_box_dut_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,15 +221,15 @@ def test_missing_grant_is_actionable_and_banner_stripped(self):

def test_bootstrap_names_the_boxes_login_user(self):
# The pasted grant must match the box's actual login user; a
# hardcoded `lagerdata` never matched on e.g. juultest boxes.
# hardcoded `lagerdata` never matched on boxes with a different user.
stderr = "sudo: a password is required to run sudo\r\n"
with patch.object(box_dut_cli, "resolve_box_user", return_value="juultest"), \
with patch.object(box_dut_cli, "resolve_box_user", return_value="benchtest"), \
patch.object(box_dut_cli.click, "secho") as secho:
_, ok = self._run(write_result=(1, "", stderr))
self.assertFalse(ok)
msg = secho.call_args[0][0]
self.assertIn(
"juultest ALL=(ALL) NOPASSWD: "
"benchtest ALL=(ALL) NOPASSWD: "
"/bin/cp /tmp/lager-bench.json.tmp /etc/lager/bench.json",
msg,
)
Expand Down
39 changes: 20 additions & 19 deletions test/unit/box/test_host_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,17 +219,18 @@ def test_default_user_matches_legacy_command(self):
self.assertEqual(ops.boxcfg_sudoers_bootstrap_cmd(), self.LEGACY_CMD)

def test_rules_name_the_given_user(self):
# The whole point of parameterizing: on a juultest box the rule must
# grant juultest, or `sudo -n apt-get` never matches (the JUL-24 bug).
rules = ops.boxcfg_sudoers_rules("juultest")
# The whole point of parameterizing: on a box whose login user isn't
# `lagerdata`, the rule must grant that user, or `sudo -n apt-get`
# never matches.
rules = ops.boxcfg_sudoers_rules("benchtest")
self.assertEqual(len(rules), 2)
for rule in rules:
self.assertTrue(rule.startswith("juultest ALL=(root) NOPASSWD: "), rule)
self.assertTrue(rule.startswith("benchtest ALL=(root) NOPASSWD: "), rule)
self.assertNotIn("lagerdata", " ".join(rules))

def test_bootstrap_cmd_interpolates_user(self):
cmd = ops.boxcfg_sudoers_bootstrap_cmd("juultest")
self.assertIn("'juultest ALL=(root) NOPASSWD: SETENV: /usr/bin/apt-get'", cmd)
cmd = ops.boxcfg_sudoers_bootstrap_cmd("benchtest")
self.assertIn("'benchtest ALL=(root) NOPASSWD: SETENV: /usr/bin/apt-get'", cmd)
self.assertNotIn("lagerdata", cmd)

def test_rules_raise_on_invalid_user(self):
Expand All @@ -246,15 +247,15 @@ def test_bootstrap_cmd_raises_on_invalid_user(self):


class UsernameValidation(unittest.TestCase):
def test_accepts_real_fleet_usernames(self):
for user in ["lagerdata", "juultest", "faunalogy-2", "_svc", "a.b-c_d", "Host$"]:
def test_accepts_valid_usernames(self):
for user in ["lagerdata", "benchtest", "lab-2", "_svc", "a.b-c_d", "Host$"]:
self.assertTrue(ops.is_valid_unix_username(user), user)

def test_rejects_injection_and_junk(self):
for user in [
None,
"",
"juul test", # space splits sudoers fields
"bad user", # space splits sudoers fields
"a'b", # would close the shell quote
"a\nb ALL=(ALL) NOPASSWD: ALL", # sudoers line injection
"$(reboot)",
Expand All @@ -267,14 +268,14 @@ def test_rejects_injection_and_junk(self):

class BootstrapTexts(unittest.TestCase):
def test_sudoers_bootstrap_names_user(self):
text = ops.sudoers_bootstrap("juultest")
self.assertIn("'juultest ALL=(root) NOPASSWD: SETENV: /usr/bin/apt-get'", text)
text = ops.sudoers_bootstrap("benchtest")
self.assertIn("'benchtest ALL=(root) NOPASSWD: SETENV: /usr/bin/apt-get'", text)
self.assertNotIn("lagerdata", text)

def test_udev_bootstrap_names_user(self):
text = ops.udev_sudoers_bootstrap("juultest")
text = ops.udev_sudoers_bootstrap("benchtest")
self.assertIn(
"juultest ALL=(ALL) NOPASSWD: /bin/cp /tmp/*.rules /etc/udev/rules.d/", text
"benchtest ALL=(ALL) NOPASSWD: /bin/cp /tmp/*.rules /etc/udev/rules.d/", text
)
# The sudoers *filename* is historical and stays lagerdata-udev
# (matching what setup_and_deploy_box.sh writes); only the rule
Expand All @@ -294,14 +295,14 @@ def test_bootstrap_texts_fall_back_on_invalid_user(self):

def test_failure_messages_carry_user(self):
runner, _ = _runner_returning(1, stderr="sudo: a password is required")
result = ops.apt_install("1.2.3.4", ["tcpdump"], ssh_runner=runner, user="juultest")
self.assertIn("juultest ALL=(root)", result.message)
result = ops.sysctl_apply("1.2.3.4", {"vm.dirty_ratio": "10"}, ssh_runner=runner, user="juultest")
self.assertIn("juultest ALL=(root)", result.message)
result = ops.apt_install("1.2.3.4", ["tcpdump"], ssh_runner=runner, user="benchtest")
self.assertIn("benchtest ALL=(root)", result.message)
result = ops.sysctl_apply("1.2.3.4", {"vm.dirty_ratio": "10"}, ssh_runner=runner, user="benchtest")
self.assertIn("benchtest ALL=(root)", result.message)
result = ops.udev_apply(
"1.2.3.4", [{"vid": "1209", "pid": "0001"}], ssh_runner=runner, user="juultest"
"1.2.3.4", [{"vid": "1209", "pid": "0001"}], ssh_runner=runner, user="benchtest"
)
self.assertIn("juultest ALL=(ALL)", result.message)
self.assertIn("benchtest ALL=(ALL)", result.message)


if __name__ == "__main__":
Expand Down
Loading