mirror of
https://github.com/netinvent/npbackup.git
synced 2025-02-24 22:44:04 +08:00
AES encryption change triggers conf file re-encryption
This commit is contained in:
parent
50a0c19aea
commit
fb25d83ebb
2 changed files with 68 additions and 14 deletions
|
@ -8,29 +8,42 @@ __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__ = "2023032601"
|
__build__ = "2023032601"
|
||||||
__version__ = "1.6.3 for npbackup 2.2.0+"
|
__version__ = "1.7.0 for npbackup 2.2.0+"
|
||||||
|
|
||||||
from typing import Tuple, Optional
|
from typing import Tuple, Optional
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
from ruamel.yaml import YAML
|
from ruamel.yaml import YAML
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
import re
|
import re
|
||||||
import platform
|
import platform
|
||||||
from cryptidy import symmetric_encryption as enc
|
from cryptidy import symmetric_encryption as enc
|
||||||
from ofunctions.random import random_string
|
from ofunctions.random import random_string
|
||||||
|
from npbackup.path_helper import BASEDIR
|
||||||
from npbackup.customization import ID_STRING
|
from npbackup.customization import ID_STRING
|
||||||
|
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.normpath(os.path.join(os.path.dirname(__file__), "..")))
|
||||||
|
|
||||||
# Try to import a private key, if not available, fallback to the default key
|
# Try to import a private key, if not available, fallback to the default key
|
||||||
try:
|
try:
|
||||||
from npbackup._private_secret_keys import AES_KEY, DEFAULT_BACKUP_ADMIN_PASSWORD
|
from PRIVATE._private_secret_keys import AES_KEY, DEFAULT_BACKUP_ADMIN_PASSWORD
|
||||||
from npbackup._private_revac import revac
|
from PRIVATE._private_revac import revac
|
||||||
AES_KEY = revac(AES_KEY)
|
AES_KEY = revac(AES_KEY)
|
||||||
IS_PRIV_BUILD = True
|
IS_PRIV_BUILD = True
|
||||||
|
try:
|
||||||
|
from PRIVATE._private_secret_keys import OLD_AES_KEY
|
||||||
|
OLD_AES_KEY = revac(OLD_AES_KEY)
|
||||||
|
except ImportError:
|
||||||
|
OLD_AES_KEY = None
|
||||||
except ImportError:
|
except ImportError:
|
||||||
try:
|
try:
|
||||||
from npbackup.secret_keys import AES_KEY, DEFAULT_BACKUP_ADMIN_PASSWORD
|
from npbackup.secret_keys import AES_KEY, DEFAULT_BACKUP_ADMIN_PASSWORD
|
||||||
IS_PRIV_BUILD = False
|
IS_PRIV_BUILD = False
|
||||||
|
try:
|
||||||
|
from npbackup.secret_keys import OLD_AES_KEY
|
||||||
|
except ImportError:
|
||||||
|
OLD_AES_KEY = None
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print("No secret_keys file. Please read documentation.")
|
print("No secret_keys file. Please read documentation.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
@ -94,6 +107,8 @@ empty_config_dict = {
|
||||||
|
|
||||||
|
|
||||||
def decrypt_data(config_dict: dict) -> dict:
|
def decrypt_data(config_dict: dict) -> dict:
|
||||||
|
if not config_dict:
|
||||||
|
return None
|
||||||
try:
|
try:
|
||||||
for option in ENCRYPTED_OPTIONS:
|
for option in ENCRYPTED_OPTIONS:
|
||||||
try:
|
try:
|
||||||
|
@ -124,12 +139,13 @@ def decrypt_data(config_dict: dict) -> dict:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
logger.debug("Trace:", exc_info=True)
|
logger.debug("Trace:", exc_info=True)
|
||||||
sys.exit(11)
|
|
||||||
except TypeError:
|
|
||||||
logger.error(
|
|
||||||
"Cannot decrypt this configuration file. No base64 encoded strings available."
|
|
||||||
)
|
|
||||||
return False
|
return False
|
||||||
|
except TypeError as exc:
|
||||||
|
logger.error(
|
||||||
|
"Cannot decrypt this configuration file. No base64 encoded strings available: {}.".format(exc)
|
||||||
|
)
|
||||||
|
logger.debug("Trace:", exc_info=True)
|
||||||
|
return None
|
||||||
return config_dict
|
return config_dict
|
||||||
|
|
||||||
|
|
||||||
|
@ -233,25 +249,57 @@ def evaluate_variables(config_dict: dict, value: str) -> str:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def update_encryption(config_file: str) -> Optional[dict]:
|
||||||
|
"""
|
||||||
|
If we changed AES KEY, let's use the old key to update the config file
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def load_config(config_file: str) -> Optional[dict]:
|
def load_config(config_file: str) -> Optional[dict]:
|
||||||
"""
|
"""
|
||||||
Using ruamel.yaml preserves comments and order of yaml files
|
Using ruamel.yaml preserves comments and order of yaml files
|
||||||
"""
|
"""
|
||||||
|
global AES_KEY
|
||||||
|
global OLD_AES_KEY
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.debug("Using configuration file {}".format(config_file))
|
logger.debug("Using configuration file {}".format(config_file))
|
||||||
with open(config_file, "r", encoding="utf-8") as file_handle:
|
with open(config_file, "r", encoding="utf-8") as file_handle:
|
||||||
# RoundTrip loader is default and preserves comments and ordering
|
# RoundTrip loader is default and preserves comments and ordering
|
||||||
yaml = YAML(typ="rt")
|
yaml = YAML(typ="rt")
|
||||||
config_dict = yaml.load(file_handle)
|
config_dict = yaml.load(file_handle)
|
||||||
|
|
||||||
|
config_file_needs_save = False
|
||||||
|
# Check modifications before decrypting since ruamel object is a pointer !!!
|
||||||
is_modified, config_dict = has_random_variables(config_dict)
|
is_modified, config_dict = has_random_variables(config_dict)
|
||||||
if is_modified:
|
if is_modified:
|
||||||
logger.info("Handling random variables in configuration files")
|
logger.info("Handling random variables in configuration files")
|
||||||
save_config(config_file, config_dict)
|
config_file_needs_save = True
|
||||||
if not is_encrypted(config_dict):
|
if not is_encrypted(config_dict):
|
||||||
logger.info("Encrypting non encrypted data in configuration file")
|
logger.info("Encrypting non encrypted data in configuration file")
|
||||||
config_dict = encrypt_data(config_dict)
|
config_file_needs_save = True
|
||||||
|
|
||||||
|
config_dict_decrypted = decrypt_data(config_dict)
|
||||||
|
if config_dict_decrypted == False:
|
||||||
|
if OLD_AES_KEY:
|
||||||
|
new_aes_key = AES_KEY
|
||||||
|
AES_KEY = OLD_AES_KEY
|
||||||
|
logger.info("Trying to migrate encryption key")
|
||||||
|
config_dict_decrypted = decrypt_data(config_dict)
|
||||||
|
if config_dict_decrypted is not False:
|
||||||
|
AES_KEY = new_aes_key
|
||||||
|
logger.info("Migrated encryption")
|
||||||
|
config_file_needs_save = True
|
||||||
|
new_aes_key = None
|
||||||
|
OLD_AES_KEY = None
|
||||||
|
else:
|
||||||
|
logger.critical("Cannot decrypt config file.")
|
||||||
|
sys.exit(12)
|
||||||
|
else:
|
||||||
|
sys.exit(11)
|
||||||
|
if config_file_needs_save:
|
||||||
|
logger.info("Updating config file")
|
||||||
save_config(config_file, config_dict)
|
save_config(config_file, config_dict)
|
||||||
config_dict = decrypt_data(config_dict)
|
|
||||||
return config_dict
|
return config_dict
|
||||||
except OSError:
|
except OSError:
|
||||||
logger.critical("Cannot load configuration file from %s", config_file)
|
logger.critical("Cannot load configuration file from %s", config_file)
|
||||||
|
|
|
@ -7,16 +7,22 @@ __intname__ = "npbackup.secret_keys"
|
||||||
__author__ = "Orsiris de Jong"
|
__author__ = "Orsiris de Jong"
|
||||||
__copyright__ = "Copyright (C) 2023 NetInvent"
|
__copyright__ = "Copyright (C) 2023 NetInvent"
|
||||||
__license__ = "GPL-3.0-only"
|
__license__ = "GPL-3.0-only"
|
||||||
__build__ = "2022120401"
|
__build__ = "2023032601"
|
||||||
|
|
||||||
|
|
||||||
# Encryption key to keep repo settings safe in plain text yaml config file
|
# Encryption key to keep repo settings safe in plain text yaml config file
|
||||||
|
|
||||||
# This is the default key that comes with NPBackup.. You should change it (and keep a backup copy in case you need to decrypt a config file data)
|
# This is the default key that comes with NPBackup.. You should change it (and keep a backup copy in case you need to decrypt a config file data)
|
||||||
# You can overwrite this by copying this file to `_private_secret_keys.py` and generating a new key
|
# You can overwrite this by copying this file to `../PRIVATE/_private_secret_keys.py` and generating a new key
|
||||||
# Obtain a new key with:
|
# Obtain a new key with:
|
||||||
# from cryptidy.symmetric_encryption import generate_key
|
# from cryptidy.symmetric_encryption import generate_key
|
||||||
# print(generate_key(32))
|
# print(generate_key(32))
|
||||||
|
|
||||||
AES_KEY = b"\x9e\xbck\xe4\xc5nkT\x1e\xbf\xb5o\x06\xd3\xc6(\x0e:'i\x1bT\xb3\xf0\x1aC e\x9bd\xa5\xc6"
|
AES_KEY = b"\xc3T\xdci\xe3[s\x87o\x96\x8f\xe5\xee.>\xf1,\x94\x8d\xfe\x0f\xea\x11\x05 \xa0\xe9S\xcf\x82\xad|"
|
||||||
DEFAULT_BACKUP_ADMIN_PASSWORD = "NPBackup_00"
|
DEFAULT_BACKUP_ADMIN_PASSWORD = "NPBackup_00"
|
||||||
|
|
||||||
|
"""
|
||||||
|
If someday we need to change the AES_KEY, copy it's content to OLD_AES_KEY and generate a new one
|
||||||
|
Keeping OLD_AES_KEY allows to migrate from old configuration files to new ones
|
||||||
|
"""
|
||||||
|
EARLIER_AES_KEY = b"\x9e\xbck\xe4\xc5nkT\x1e\xbf\xb5o\x06\xd3\xc6(\x0e:'i\x1bT\xb3\xf0\x1aC e\x9bd\xa5\xc6"
|
||||||
|
|
Loading…
Reference in a new issue