From 480bd7f8459c1c3eb38b2571088ba44a8ad9f308 Mon Sep 17 00:00:00 2001 From: Ana Custura Date: Thu, 5 Mar 2026 09:14:17 +0000 Subject: [PATCH] Add a script to implement disabling services and changing various bits of config --- app/change_manager.py | 145 ++++++++++++++++++++++++++++++++++++++++++ app/routes.py | 9 ++- 2 files changed, 149 insertions(+), 5 deletions(-) create mode 100644 app/change_manager.py diff --git a/app/change_manager.py b/app/change_manager.py new file mode 100644 index 0000000..1ef4119 --- /dev/null +++ b/app/change_manager.py @@ -0,0 +1,145 @@ +import os +import re +from subprocess import run +import json +from app import app + +CHANGES_REQUIRING_RESTART = ['wifi_password', 'ssid', 'enable_access_point', 'enable_chat', 'enable_delta_chat', 'butterbox_hostname'] + # 'ssh_access_settings', 'root_account_settings'] + +def lock_root_account(): + result = run(["sudo", "passwd", "-l", "root"]) + if result.returncode != 0: + return False + return True + +def enable_service(service: str): + is_enabled = run(["sudo", "systemctl", "is-enabled", service], capture_output = True, text = True) + if 'disabled' in is_enabled.stdout: + enable = run(["sudo", "systemctl", "enable", service], capture_output = True, text = True) + if enable.returncode != 0: + return False + return True + +def disable_service(service: str): + is_enabled = run(["sudo", "systemctl", "is-enabled", service], capture_output = True, text = True) + if 'enabled' in is_enabled.stdout: + result = run(["sudo", "systemctl", "disable", service]) + if result.returncode != 0: + return False + return True + +def load_setting(setting): + with open("settings.txt", "r") as f: + settings = json.load(f) + return settings[setting] + +def change_service_status(setting, service): + if load_setting(setting) == "true": + enable_service(service) + else: + disable_service(service) + +def change_keanu_weblite_config(new_hostname): + regex = re.compile('qdt ?= ?"([^"]*)"') + target_file = "/var/www/html/chat/assets/index-CD0QtET8.js" + with open(target_file, "r") as f: + lines = f.readlines() + for line in enumerate(lines): + match = regex.search(line) + if match: + old_hostname = match.group(1) + if old_hostname != new_hostname: + re.sub(old_hostname, new_hostname, lines[line]) + + +def change_line_in_file(target_file: str, regex: str, replacement: str): + print(f"Changing line in file {target_file}") + regex = re.compile(regex) + if not os.path.isfile(target_file): + raise FileNotFoundError(f"File {target_file} does not exist") + else: + with open(target_file, "r") as f: + lines = f.readlines() + print(f"Existing lines are: {lines}") + for i, line in enumerate(lines): + match = re.fullmatch(replacement, line) + if match: + print("Line already exists!") + return + for i, line in enumerate(lines): + match = re.fullmatch(regex, line) + if match: + lines.pop(i) + print(f"Found a match at line {i}, {match.string}") + break + try: + lines.append(replacement) + except NameError: + raise NameError(f"File {target_file} is empty.") + print(f"Lines to be written to file are {lines}") + new_lines = "".join(lines) + with open(target_file, "w") as f: + f.write(new_lines) + + +def check_settings(): + print(f"Checking settings...") + print(f"App config is {app.config['SETTINGS_CHANGED']}") + if app.config['SETTINGS_CHANGED']: + for s in CHANGES_REQUIRING_RESTART: + if s == "wifi_password": + regex_wpa_method = "wpa=.*?\n" + if load_setting("wifi_password") == "": + change_line_in_file("/etc/hostapd/hostapd.conf", regex_wpa_method, f"wpa=none\n") + else: + change_line_in_file("/etc/hostapd/hostapd.conf", regex_wpa_method, f"wpa=3\n") + regex_pass = "wpa_passphrase=.*?\n" + change_line_in_file("/etc/hostapd/hostapd.conf", regex_pass, + f"wpa_passphrase={load_setting("wifi_password")}\n") + + if s == "ssid": + regex_ssid = "ssid=.*?\n" + change_line_in_file("/etc/hostapd/hostapd.conf", regex_ssid, f"ssid={load_setting("ssid")}\n") + if s == "enable_chat": + change_service_status("enable_chat", "dendrite") + if s == "enable_access_point": + change_service_status("enable_access_point", "raspapd") + if s == "enable_delta_chat": + change_service_status("enable_delta_chat", "madmail") + if s == "butterbox_hostname": + pass + # change in butterbox-dendrite.conf + regex_matrix_server = "server_name:.*?.lan\n" + change_line_in_file("../dendrite/butterbox-dendrite.conf", regex_matrix_server, + f"server_name: {load_setting("butterbox_hostname")}.lan\n") + # change in butterbox-dnsmasq.conf + regex_dns = "address=/.*?.lan/10.3.141.1\n" + change_line_in_file("/etc/dnsmasq.d/butterbox-dnsmasq.conf", regex_dns, + f"address=/{load_setting("butterbox_hostname")}.lan/10.3.141.1\n") + # change in keanu-weblite compiled assets + change_keanu_weblite_config(load_setting("butterbox_hostname")) + if s == "ssh_access_settings": + change_service_status("ssh_enabled", "ssh") + if load_setting("ssh_access_settings") == "enable_ssh_with_password": + regex_password_auth = "PasswordAuthentication.*?\n" + change_line_in_file("test/sshd/sshd.conf", regex_password_auth, + f"PasswordAuthentication yes\n") + regex_root_login = "PermitRootLogin.*?\n" + change_line_in_file("test/sshd/sshd.conf", regex_root_login, + f"PermitRootLogin yes\n") + elif load_setting("ssh_access_settings") == "enable_ssh_with_public_key": + regex_password_auth = "PasswordAuthentication.*?\n" + change_line_in_file("test/sshd/sshd.conf", regex_password_auth, + f"PasswordAuthentication no\n") + regex_root_login = "PermitRootLogin.*?\n" + change_line_in_file("test/sshd/sshd.conf", regex_root_login, + f"PermitRootLogin prohibit-password\n") + # append here new key!!! + if s == "root_account_settings": + if load_setting("root_account_settings") == "lock_root_account": + lock_root_account() + else: + pass + + diff --git a/app/routes.py b/app/routes.py index 02590b7..b970d69 100644 --- a/app/routes.py +++ b/app/routes.py @@ -16,7 +16,7 @@ import string import glob import time import qrcode -from datetime import datetime +from app.change_manager import CHANGES_REQUIRING_RESTART, check_settings def gen_username() -> str: words = top_n_list("en", 5000) @@ -156,10 +156,9 @@ def logout(): @login_required def admin(): form = SettingsForm() - populate_settings = ['butterbox_name', 'wifi_password', 'ssid', 'butterbox_hostname'] + populate_settings = ['butterbox_name', 'wifi_password', 'ssid', 'butterbox_hostname', 'root_account_settings', 'ssh_access_settings'] bool_settings = ['enable_access_point','enable_file_viewer', 'enable_map_viewer', 'enable_app_store', 'enable_chat', 'enable_deltachat', 'enable_wifi_sharing'] populate_settings.extend(bool_settings) - if not form.is_submitted(): for s in populate_settings: if s in bool_settings: @@ -181,8 +180,7 @@ def admin(): print(f"New value was changed for {s}. Existing value was {existing_value}, new value was {new_value}") set_setting(s, new_value) non_admin_settings_changed = True - if s in ['butterbox_name', 'wifi_password', 'ssid', 'enable_access_point', 'enable_chat', 'enable_delta_chat', 'butterbox_hostname', - 'ssh_access_settings', 'root_account_settings']: + if s in CHANGES_REQUIRING_RESTART: app.config['SETTINGS_CHANGED'] = True new_logo = form.butterbox_logo.data @@ -216,6 +214,7 @@ def admin(): if form.apply_changes.data: set_setting('apply_changes', "true") dump_settings("settings.txt") + check_settings() flash(_("⚠️ Changes applied! Please wait for the box to restart.")) return render_template('admin.html', get_setting=get_setting, form=form)