diff --git a/Dockerfile b/Dockerfile index 8732ddd0..afe05884 100755 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ ENV PATH="/opt/venv/bin:$PATH" COPY . ${INSTALL_DIR}/ -RUN pip install requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython \ +RUN pip install requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython pycryptodome \ && bash -c "find ${INSTALL_DIR} -type d -exec chmod 750 {} \;" \ && bash -c "find ${INSTALL_DIR} -type f -exec chmod 640 {} \;" \ && bash -c "find ${INSTALL_DIR} -type f \( -name '*.sh' -o -name '*.py' -o -name 'speedtest-cli' \) -exec chmod 750 {} \;" diff --git a/Dockerfile.debian b/Dockerfile.debian index 3d8a4250..b501cc00 100755 --- a/Dockerfile.debian +++ b/Dockerfile.debian @@ -42,7 +42,7 @@ RUN phpenmod -v 8.2 sqlite3 RUN apt-get install -y python3-venv RUN python3 -m venv myenv -RUN /bin/bash -c "source myenv/bin/activate && update-alternatives --install /usr/bin/python python /usr/bin/python3 10 && pip3 install requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython" +RUN /bin/bash -c "source myenv/bin/activate && update-alternatives --install /usr/bin/python python /usr/bin/python3 10 && pip3 install requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython pycryptodome" # Create a buildtimestamp.txt to later check if a new version was released RUN date +%s > ${INSTALL_DIR}/front/buildtimestamp.txt diff --git a/front/plugins/sync/config.json b/front/plugins/sync/config.json index af2c10b1..8e13c4a3 100755 --- a/front/plugins/sync/config.json +++ b/front/plugins/sync/config.json @@ -432,6 +432,26 @@ } ] }, + { + "function": "encryption_key", + "type": "text", + "maxLength": 50, + "default_value": "", + "options": [], + "localized": ["name", "description"], + "name": [ + { + "language_code": "en_us", + "string": "Encryption Key" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "Encryption key used to encrypt the sent data. The key needs to be the same on the hub and on the nodes." + } + ] + }, { "function": "CMD", "type": "readonly", diff --git a/front/plugins/sync/hub.php b/front/plugins/sync/hub.php index d2f3fabc..1e602708 100755 --- a/front/plugins/sync/hub.php +++ b/front/plugins/sync/hub.php @@ -3,6 +3,30 @@ // External files require '/app/front/php/server/init.php'; + +function decrypt_data($encoded_data) { + // Base64 decode the encoded data + $decoded_data = base64_decode($encoded_data); + + // Extract the initialization vector (IV) from the decoded data + $iv = substr($decoded_data, 0, 16); + + // Extract the actual encrypted data + $encrypted_data = substr($decoded_data, 16); + + // Get the encryption key from the settings + $key = hash('sha256', getSettingValue('SYNC_encryption_key'), true); + + // Decrypt the data + $decrypted_data = openssl_decrypt($encrypted_data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv); + + if ($decrypted_data === false) { + return null; // Decryption failed + } + + return $decrypted_data; +} + if ($_SERVER['REQUEST_METHOD'] === 'POST') { // Retrieve the authorization header $headers = apache_request_headers(); @@ -22,7 +46,15 @@ $plugin_folder = $_POST['plugin_folder'] ?? ''; $node_name = $_POST['node_name'] ?? ''; - $decoded_data = hex2bin($data); + $decoded_data = decrypt_data($data); + + if ($decrypted_data === false or $decrypted_data === null) { + write_notification("[Plugin: Sync hub API] Bad Request: Decryption failed", "alert"); + http_response_code(400); + echo 'Bad Request: Decryption failed'; + exit; + } + $storage_path = "/app/front/plugins/{$plugin_folder}"; // Create the storage directory if it doesn't exist diff --git a/front/plugins/sync/sync.py b/front/plugins/sync/sync.py index 113e8bf7..91d6cef3 100755 --- a/front/plugins/sync/sync.py +++ b/front/plugins/sync/sync.py @@ -14,7 +14,7 @@ from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64 from plugin_utils import get_plugins_configs from logger import mylog -from helper import timeNowTZ, get_setting_value +from helper import timeNowTZ, get_setting_value, encrypt_data # Define the current path and log file paths CUR_PATH = str(pathlib.Path(__file__).parent.resolve()) @@ -23,12 +23,6 @@ pluginName = 'SYNC' -# Function to encrypt data using a password -def encrypt_data(data, password): - key = hashlib.sha256(password.encode()).digest() - cipher = hashlib.pbkdf2_hmac('sha256', data.encode(), key, 100000) - return cipher.hex() - def main(): mylog('verbose', [f'[{pluginName}] In script']) @@ -38,7 +32,8 @@ def main(): # Retrieve configuration settings plugins_to_sync = get_setting_value('SYNC_plugins') - api_token = get_setting_value('SYNC_api_token') # Use an API token instead of a password + api_token = get_setting_value('SYNC_api_token') + encryption_key = get_setting_value('SYNC_encryption_key') hub_url = get_setting_value('SYNC_hub_url') node_name = get_setting_value('SYNC_node_name') @@ -63,9 +58,14 @@ def main(): if os.path.exists(file_path): # Read the content of the log file with open(file_path, 'r') as f: - newLines = f.read() - # Encrypt the log data using the API token - encrypted_data = encrypt_data(newLines, api_token) + file_content = f.read() + + mylog('verbose', [f'[{pluginName}] Sending file_content: "{file_content}"']) + + # Encrypt the log data using the encryption_key + encrypted_data = encrypt_data(file_content, encryption_key) + + mylog('verbose', [f'[{pluginName}] Sending encrypted_data: "{encrypted_data}"']) # Prepare the data payload for the POST request data = { @@ -84,7 +84,7 @@ def main(): if response.status_code == 200: mylog('verbose', [f'[{pluginName}] Data for "{plugin_folder}" sent successfully']) else: - mylog('error', [f'[{pluginName}] Failed to send data for "{plugin_folder}"']) + mylog('verbose', [f'[{pluginName}] Failed to send data for "{plugin_folder}"']) # log result plugin_objects.add_object( diff --git a/install/install_dependencies.debian.sh b/install/install_dependencies.debian.sh index 84ae5230..6c88a2c3 100755 --- a/install/install_dependencies.debian.sh +++ b/install/install_dependencies.debian.sh @@ -30,5 +30,5 @@ source myenv/bin/activate update-alternatives --install /usr/bin/python python /usr/bin/python3 10 # install packages thru pip3 -pip3 install requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython +pip3 install requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython pycryptodome diff --git a/server/helper.py b/server/helper.py index 2cee7055..76995f3a 100755 --- a/server/helper.py +++ b/server/helper.py @@ -13,6 +13,11 @@ import time from pathlib import Path import requests +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad +import base64 +import hashlib + import conf from const import * @@ -798,6 +803,13 @@ def collect_lang_strings(json, pref, stringSqlParams): return stringSqlParams +def encrypt_data(data, key): + key = hashlib.sha256(key.encode()).digest() # Ensure the key is 32 bytes long + cipher = AES.new(key, AES.MODE_CBC) # Use CBC mode for encryption + iv = cipher.iv # Initialization vector + encrypted_data = cipher.encrypt(pad(data.encode(), AES.block_size)) + return base64.b64encode(iv + encrypted_data).decode('utf-8') + #------------------------------------------------------------------------------- # Misc #-------------------------------------------------------------------------------