mirror of
https://github.com/netinvent/npbackup.git
synced 2025-10-09 05:01:13 +08:00
Implement password command
This commit is contained in:
parent
f51dcefde5
commit
90c43ff8cd
7 changed files with 35 additions and 7 deletions
|
@ -28,6 +28,7 @@ backup:
|
||||||
repo:
|
repo:
|
||||||
repository:
|
repository:
|
||||||
password:
|
password:
|
||||||
|
password_command:
|
||||||
# Backup age, in minutes, which is the minimum time between two backups
|
# Backup age, in minutes, which is the minimum time between two backups
|
||||||
minimum_backup_age: 1440
|
minimum_backup_age: 1440
|
||||||
upload_speed: 0 # in KiB, use 0 for unlimited upload speed
|
upload_speed: 0 # in KiB, use 0 for unlimited upload speed
|
||||||
|
|
|
@ -28,6 +28,7 @@ backup:
|
||||||
repo:
|
repo:
|
||||||
repository:
|
repository:
|
||||||
password:
|
password:
|
||||||
|
password_command:
|
||||||
# Backup age, in minutes, which is the minimum time between two backups
|
# Backup age, in minutes, which is the minimum time between two backups
|
||||||
minimum_backup_age: 1440
|
minimum_backup_age: 1440
|
||||||
upload_speed: 0 # in KiB, use 0 for unlimited upload speed
|
upload_speed: 0 # in KiB, use 0 for unlimited upload speed
|
||||||
|
|
|
@ -7,7 +7,7 @@ __intname__ = "npbackup.configuration"
|
||||||
__author__ = "Orsiris de Jong"
|
__author__ = "Orsiris de Jong"
|
||||||
__copyright__ = "Copyright (C) 2022-2023 NetInvent"
|
__copyright__ = "Copyright (C) 2022-2023 NetInvent"
|
||||||
__license__ = "GPL-3.0-only"
|
__license__ = "GPL-3.0-only"
|
||||||
__build__ = "2023041201"
|
__build__ = "2023050301"
|
||||||
__version__ = "1.7.0 for npbackup 2.2.0+"
|
__version__ = "1.7.0 for npbackup 2.2.0+"
|
||||||
|
|
||||||
from typing import Tuple, Optional, List
|
from typing import Tuple, Optional, List
|
||||||
|
@ -54,9 +54,11 @@ except ImportError:
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
|
|
||||||
|
# NPF-SEC-00003: Avoid password command divulgation
|
||||||
ENCRYPTED_OPTIONS = [
|
ENCRYPTED_OPTIONS = [
|
||||||
{"section": "repo", "name": "repository", "type": str},
|
{"section": "repo", "name": "repository", "type": str},
|
||||||
{"section": "repo", "name": "password", "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_username", "type": str},
|
||||||
{"section": "prometheus", "name": "http_password", "type": str},
|
{"section": "prometheus", "name": "http_password", "type": str},
|
||||||
{"section": "options", "name": "auto_upgrade_server_username", "type": str},
|
{"section": "options", "name": "auto_upgrade_server_username", "type": str},
|
||||||
|
@ -82,6 +84,7 @@ empty_config_dict = {
|
||||||
"repo": {
|
"repo": {
|
||||||
"repository": "",
|
"repository": "",
|
||||||
"password": "",
|
"password": "",
|
||||||
|
"password_command": "",
|
||||||
"minimum_backup_age": 1440,
|
"minimum_backup_age": 1440,
|
||||||
"upload_speed": 0,
|
"upload_speed": 0,
|
||||||
"download_speed": 0,
|
"download_speed": 0,
|
||||||
|
|
|
@ -111,6 +111,8 @@ class NPBackupRunner:
|
||||||
"""
|
"""
|
||||||
Wraps ResticRunner into a class that is usable by NPBackup
|
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):
|
def __init__(self, config_dict):
|
||||||
self.config_dict = config_dict
|
self.config_dict = config_dict
|
||||||
|
@ -214,12 +216,27 @@ class NPBackupRunner:
|
||||||
logger.error("Repo cannot be empty")
|
logger.error("Repo cannot be empty")
|
||||||
can_run = False
|
can_run = False
|
||||||
try:
|
try:
|
||||||
password = self.config_dict["repo"]["password"]
|
password = self.config_dict["repo"]["password"]
|
||||||
if not password:
|
|
||||||
raise KeyError
|
|
||||||
except (KeyError, AttributeError):
|
except (KeyError, AttributeError):
|
||||||
logger.error("Repo password cannot be empty")
|
logger.error("Repo password cannot be empty")
|
||||||
can_run = False
|
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
|
self.is_ready = can_run
|
||||||
if not can_run:
|
if not can_run:
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -66,12 +66,14 @@ def config_gui(config_dict: dict, config_file: str):
|
||||||
try:
|
try:
|
||||||
value = config_dict[section][entry]
|
value = config_dict[section][entry]
|
||||||
# Don't show sensible info unless unencrypted requested
|
# Don't show sensible info unless unencrypted requested
|
||||||
|
# TODO: Refactor this to use ENCRYPTED_OPTIONS from configuration
|
||||||
if not unencrypted:
|
if not unencrypted:
|
||||||
if entry in [
|
if entry in [
|
||||||
"http_username",
|
"http_username",
|
||||||
"http_password",
|
"http_password",
|
||||||
"repository",
|
"repository",
|
||||||
"password",
|
"password",
|
||||||
|
"password_command",
|
||||||
"auto_upgrade_server_username",
|
"auto_upgrade_server_username",
|
||||||
"auto_upgrade_server_password",
|
"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.Text(_t("config_gui.backup_repo_password"), size=(30, 1)),
|
||||||
sg.Input(key="repo---password", size=(50, 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.Text(_t("config_gui.upload_speed"), size=(30, 1)),
|
||||||
sg.Input(key="repo---upload_speed", size=(50, 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"):
|
if event in (sg.WIN_CLOSED, "cancel"):
|
||||||
break
|
break
|
||||||
if event == "accept":
|
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"))
|
sg.PopupError(_t("config_gui.repo_password_cannot_be_empty"))
|
||||||
continue
|
continue
|
||||||
config_dict = update_config_dict(values, config_dict)
|
config_dict = update_config_dict(values, config_dict)
|
||||||
|
|
|
@ -53,7 +53,7 @@ en:
|
||||||
|
|
||||||
configuration_saved: Configuration saved
|
configuration_saved: Configuration saved
|
||||||
cannot_save_configuration: Could not save configuration. See logs for further info
|
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
|
enter_backup_admin_password: Backup admin password
|
||||||
wrong_password: Wrong password
|
wrong_password: Wrong password
|
||||||
password_updated_please_save: Password updated. Please save configuration
|
password_updated_please_save: Password updated. Please save configuration
|
||||||
|
|
|
@ -53,7 +53,7 @@ fr:
|
||||||
|
|
||||||
configuration_saved: Configuration sauvegardée
|
configuration_saved: Configuration sauvegardée
|
||||||
cannot_save_configuration: Impossible d'enregistrer la configuration. Veuillez consulter les journaux pour plus de détails
|
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
|
enter_backup_admin_password: Mot de passe admin de sauvegarde
|
||||||
wrong_password: Mot de passe érroné
|
wrong_password: Mot de passe érroné
|
||||||
password_updated_please_save: Mot de passe mis à jour. Veuillez enregistrer la configuraiton
|
password_updated_please_save: Mot de passe mis à jour. Veuillez enregistrer la configuraiton
|
||||||
|
|
Loading…
Add table
Reference in a new issue