Implement password command

This commit is contained in:
Orsiris de Jong 2023-05-03 18:50:19 +02:00
parent f51dcefde5
commit 90c43ff8cd
7 changed files with 35 additions and 7 deletions

View file

@ -28,6 +28,7 @@ backup:
repo:
repository:
password:
password_command:
# Backup age, in minutes, which is the minimum time between two backups
minimum_backup_age: 1440
upload_speed: 0 # in KiB, use 0 for unlimited upload speed

View file

@ -28,6 +28,7 @@ backup:
repo:
repository:
password:
password_command:
# Backup age, in minutes, which is the minimum time between two backups
minimum_backup_age: 1440
upload_speed: 0 # in KiB, use 0 for unlimited upload speed

View file

@ -7,7 +7,7 @@ __intname__ = "npbackup.configuration"
__author__ = "Orsiris de Jong"
__copyright__ = "Copyright (C) 2022-2023 NetInvent"
__license__ = "GPL-3.0-only"
__build__ = "2023041201"
__build__ = "2023050301"
__version__ = "1.7.0 for npbackup 2.2.0+"
from typing import Tuple, Optional, List
@ -54,9 +54,11 @@ except ImportError:
logger = getLogger(__name__)
# NPF-SEC-00003: Avoid password command divulgation
ENCRYPTED_OPTIONS = [
{"section": "repo", "name": "repository", "type": str},
{"section": "repo", "name": "password", "type": str},
{"section": "repo", "name": "password_command", "type": str},
{"section": "prometheus", "name": "http_username", "type": str},
{"section": "prometheus", "name": "http_password", "type": str},
{"section": "options", "name": "auto_upgrade_server_username", "type": str},
@ -82,6 +84,7 @@ empty_config_dict = {
"repo": {
"repository": "",
"password": "",
"password_command": "",
"minimum_backup_age": 1440,
"upload_speed": 0,
"download_speed": 0,

View file

@ -111,6 +111,8 @@ class NPBackupRunner:
"""
Wraps ResticRunner into a class that is usable by NPBackup
"""
# NPF-SEC-00002: password commands, pre_exec and post_exec commands will be executed with npbackup privileges
# This can lead to a problem when the config file can be written by users other than npbackup
def __init__(self, config_dict):
self.config_dict = config_dict
@ -215,11 +217,26 @@ class NPBackupRunner:
can_run = False
try:
password = self.config_dict["repo"]["password"]
if not password:
raise KeyError
except (KeyError, AttributeError):
logger.error("Repo password cannot be empty")
can_run = False
if not password or password == "":
try:
password_command = self.config_dict["repo"]["password_command"]
if password_command and password_command != "":
exit_code, output = command_runner(password_command, shell=True, timeout=30)
if exit_code != 0 or output == "":
logger.error("Password command failed to produce output:\n{}".format(output))
can_run = False
else:
password = output
else:
logger.error("No password nor password command given. Repo password cannot be empty")
can_run = False
except KeyError:
logger.error("No password nor password command given. Repo password cannot be empty")
can_run = False
print(password)
self.is_ready = can_run
if not can_run:
return None

View file

@ -66,12 +66,14 @@ def config_gui(config_dict: dict, config_file: str):
try:
value = config_dict[section][entry]
# Don't show sensible info unless unencrypted requested
# TODO: Refactor this to use ENCRYPTED_OPTIONS from configuration
if not unencrypted:
if entry in [
"http_username",
"http_password",
"repository",
"password",
"password_command",
"auto_upgrade_server_username",
"auto_upgrade_server_password",
]:
@ -251,6 +253,10 @@ def config_gui(config_dict: dict, config_file: str):
sg.Text(_t("config_gui.backup_repo_password"), size=(30, 1)),
sg.Input(key="repo---password", size=(50, 1)),
],
[
sg.Text(_t("config.gui.backup_repo_password_command"), size=(30, 1)),
sg.Input(key="repo---password_command", size=(50, 1))
],
[
sg.Text(_t("config_gui.upload_speed"), size=(30, 1)),
sg.Input(key="repo---upload_speed", size=(50, 1)),
@ -483,7 +489,7 @@ def config_gui(config_dict: dict, config_file: str):
if event in (sg.WIN_CLOSED, "cancel"):
break
if event == "accept":
if not values["repo---password"]:
if not values["repo---password"] and not values["repo---password_command"]:
sg.PopupError(_t("config_gui.repo_password_cannot_be_empty"))
continue
config_dict = update_config_dict(values, config_dict)

View file

@ -53,7 +53,7 @@ en:
configuration_saved: Configuration saved
cannot_save_configuration: Could not save configuration. See logs for further info
repo_password_cannot_be_empty: Password cannot be empty
repo_password_cannot_be_empty: Repo password or password command cannot be empty
enter_backup_admin_password: Backup admin password
wrong_password: Wrong password
password_updated_please_save: Password updated. Please save configuration

View file

@ -53,7 +53,7 @@ fr:
configuration_saved: Configuration sauvegardée
cannot_save_configuration: Impossible d'enregistrer la configuration. Veuillez consulter les journaux pour plus de détails
repo_password_cannot_be_empty: Le mot de passe du dépot ne peut être vide
repo_password_cannot_be_empty: Le mot de passe du dépot ou la commande de mot de passe ne peut être vide
enter_backup_admin_password: Mot de passe admin de sauvegarde
wrong_password: Mot de passe érroné
password_updated_please_save: Mot de passe mis à jour. Veuillez enregistrer la configuraiton