Add option to run sudo -v in a loop to prevent repeated password entries (MR 1997)

Many of pmbootstrap's actions require root rights. When after requesting
sudo access pmbootstrap takes longer than the sudo timeout interval to finish
execution, the password will have to be entered again on the next sudo
action.

This change adds an opt-in feature to run sudo -v in a background loop
in order to prevent having to enter the password more than once for a single
pmbootstrap run. The loop runs as a daemon timer which automatically gets
canceled when pmbootstrap exits.

Closes: #1677
This commit is contained in:
Johannes Marbach 2020-11-30 14:08:29 +01:00
parent 8842a7d5c0
commit 1eac61bcf7
No known key found for this signature in database
GPG Key ID: 5AE7F5513E0885CB
5 changed files with 49 additions and 11 deletions

View File

@ -60,7 +60,8 @@ config_keys = ["aports",
"user",
"work",
"boot_size",
"extra_space"]
"extra_space",
"sudo_timer"]
# Config file/commandline default values
# $WORK gets replaced with the actual value for args.work (which may be
@ -96,7 +97,8 @@ defaults = {
"user": "user",
"work": os.path.expanduser("~") + "/.local/var/pmbootstrap",
"boot_size": "128",
"extra_space": "0"
"extra_space": "0",
"sudo_timer": False
}

View File

@ -344,7 +344,8 @@ def ask_for_additional_options(args, cfg):
f" extra free space: {args.extra_space} MB,"
f" boot partition size: {args.boot_size} MB,"
f" parallel jobs: {args.jobs},"
f" ccache per arch: {args.ccache_size}")
f" ccache per arch: {args.ccache_size},"
f" sudo timer: {args.sudo_timer}")
if not pmb.helpers.cli.confirm(args, "Change them?",
default=False):
@ -385,6 +386,17 @@ def ask_for_additional_options(args, cfg):
lowercase_answer=False, validation_regex=regex)
cfg["pmbootstrap"]["ccache_size"] = answer
# Sudo timer
logging.info("pmbootstrap does everything in Alpine Linux chroots, so"
" your host system does not get modified. In order to"
" work with these chroots, pmbootstrap calls 'sudo'"
" internally. For long running operations, it is possible"
" that you'll have to authorize sudo more than once.")
answer = pmb.helpers.cli.confirm(args, "Enable background timer to prevent"
" repeated sudo authorization?",
default=args.sudo_timer)
cfg["pmbootstrap"]["sudo_timer"] = str(answer)
def ask_for_hostname(args, device):
while True:

View File

@ -23,12 +23,6 @@ def check_legacy_folder():
def clone(args):
# Explain sudo-usage before using it the first time
logging.info("pmbootstrap does everything in Alpine Linux chroots, so your"
" host system does not get modified. In order to work with"
" these chroots, pmbootstrap calls 'sudo' internally. To see"
" the commands it runs, you can run 'pmbootstrap log' in a"
" second terminal.")
logging.info("Setting up the native chroot and cloning the package build"
" recipes (pmaports)...")

View File

@ -5,6 +5,7 @@ import logging
import selectors
import subprocess
import sys
import threading
import time
import os
import pmb.helpers.run
@ -217,6 +218,31 @@ def check_return_code(args, code, log_message):
raise RuntimeError("Command failed: " + log_message)
def sudo_timer_iterate():
"""
Run sudo -v and schedule a new timer to repeat the same.
"""
subprocess.Popen(["sudo", "-v"]).wait()
timer = threading.Timer(interval=60, function=sudo_timer_iterate)
timer.daemon = True
timer.start()
def sudo_timer_start(args):
"""
Start a timer to call sudo -v periodically, so that the password is only
needed once.
"""
if "sudo_timer_active" in args.cache:
return
args.cache["sudo_timer_active"] = True
sudo_timer_iterate()
def core(args, log_message, cmd, working_dir=None, output="log",
output_return=False, check=None, sudo=False, disable_timeout=False):
"""
@ -277,6 +303,9 @@ def core(args, log_message, cmd, working_dir=None, output="log",
"""
sanity_checks(output, output_return, check)
if args.sudo_timer and sudo:
sudo_timer_start(args)
# Log simplified and full command (pmbootstrap -v)
logging.debug(log_message)
logging.verbose("run: " + str(cmd))

View File

@ -261,12 +261,13 @@ def test_questions_additional_options(args, monkeypatch):
assert cfg == {"pmbootstrap": {}}
# Answer everything
fake_answers(monkeypatch, ["y", "128", "64", "5", "2G", "n"])
fake_answers(monkeypatch, ["y", "128", "64", "5", "2G", "n", "n"])
func(args, cfg)
assert cfg == {"pmbootstrap": {"extra_space": "128",
"boot_size": "64",
"jobs": "5",
"ccache_size": "2G"}}
"ccache_size": "2G",
"sudo_timer": "False"}}
def test_questions_hostname(args, monkeypatch):