diff --git a/config/sampleconfig.toml b/config/sampleconfig.toml index 5935853..068b217 100644 --- a/config/sampleconfig.toml +++ b/config/sampleconfig.toml @@ -132,5 +132,10 @@ authentication_timeout = 15 # in minutes restrict_su = true [pam] # PAM +enable_password_level = true required_password_level = "strong" # weak, medium, strong, stronger +enable_password_length = true minimum_password_length = 14 +limit_password_reuse = true +password_reuse_limit = 5 +configure_hashing_algorithm = true diff --git a/config/server/level-1.toml b/config/server/level-1.toml index eee62f2..3497389 100644 --- a/config/server/level-1.toml +++ b/config/server/level-1.toml @@ -124,8 +124,17 @@ client_alive_count_max = 3 [privilege_escalation] # Privilege Escalation use_pty = true enable_logfile = true -disable_nopasswd = false +disable_nopassword = false enable_reauthentication = true enable_authentication_timeout = true authentication_timeout = 15 # in minutes -restrict_su = true \ No newline at end of file +restrict_su = true + +[pam] # PAM +enable_password_level = true +required_password_level = "strong" # weak, medium, strong, stronger +enable_password_length = true +minimum_password_length = 14 +limit_password_reuse = true +password_reuse_limit = 5 +configure_hashing_algorithm = true diff --git a/config/server/level-2.toml b/config/server/level-2.toml index 5858962..9769535 100644 --- a/config/server/level-2.toml +++ b/config/server/level-2.toml @@ -123,8 +123,17 @@ client_alive_count_max = 3 [privilege_escalation] # Privilege Escalation use_pty = true enable_logfile = true -disable_nopasswd = false +disable_nopassword = false enable_reauthentication = true enable_authentication_timeout = true authentication_timeout = 15 # in minutes -restrict_su = true \ No newline at end of file +restrict_su = true + +[pam] # PAM +enable_password_level = true +required_password_level = "strong" # weak, medium, strong, stronger +enable_password_length = true +minimum_password_length = 14 +limit_password_reuse = true +password_reuse_limit = 5 +configure_hashing_algorithm = true diff --git a/config/tooltip.toml b/config/tooltip.toml index 219a579..64f406b 100644 --- a/config/tooltip.toml +++ b/config/tooltip.toml @@ -125,4 +125,13 @@ disable_nopassword = "false" enable_reauthentication = "true" enable_authentication_timeout = "true" authentication_timeout = "15" # in minutes -restrict_su = "true" \ No newline at end of file +restrict_su = "true" + +[pam] # PAM +enable_password_level = "true" +required_password_level = "strong" # weak, medium, strong, stronger +enable_password_length = "true" +minimum_password_length = "14" +limit_password_reuse = "true" +password_reuse_limit = "5" +configure_hashing_algorithm = "true" diff --git a/config/workstation/level-1.toml b/config/workstation/level-1.toml index 8978cc8..606dc8b 100644 --- a/config/workstation/level-1.toml +++ b/config/workstation/level-1.toml @@ -120,8 +120,17 @@ client_alive_count_max = {enable = true, value = 3} [privilege_escalation] # Privilege Escalation use_pty = true enable_logfile = true -disable_nopasswd = false +disable_nopassword = false enable_reauthentication = true enable_authentication_timeout = true authentication_timeout = 15 # in minutes -restrict_su = true \ No newline at end of file +restrict_su = true + +[pam] # PAM +enable_password_level = true +required_password_level = "strong" # weak, medium, strong, stronger +enable_password_length = true +minimum_password_length = 14 +limit_password_reuse = true +password_reuse_limit = 5 +configure_hashing_algorithm = true diff --git a/config/workstation/level-2.toml b/config/workstation/level-2.toml index d81e663..edf5364 100644 --- a/config/workstation/level-2.toml +++ b/config/workstation/level-2.toml @@ -123,8 +123,17 @@ client_alive_count_max = 3 [privilege_escalation] # Privilege Escalation use_pty = true enable_logfile = true -disable_nopasswd = false +disable_nopassword = false enable_reauthentication = true enable_authentication_timeout = true authentication_timeout = 15 # in minutes -restrict_su = true \ No newline at end of file +restrict_su = true + +[pam] # PAM +enable_password_level = true +required_password_level = "strong" # weak, medium, strong, stronger +enable_password_length = true +minimum_password_length = 14 +limit_password_reuse = true +password_reuse_limit = 5 +configure_hashing_algorithm = true diff --git a/ui/components/software/gdm.py b/ui/components/software/gdm.py index a34faa0..9fedf06 100644 --- a/ui/components/software/gdm.py +++ b/ui/components/software/gdm.py @@ -43,9 +43,9 @@ def init_ui(self): hlayout = QHBoxLayout() # Lock on Idle Label - self.lockon_lable = QLabel('Lock on Idle(seconds)') - self.lockon_lable.setToolTip(self.gdm_tooltip['lock_on_idle']) - self.lockon_lable.setProperty('class', 'normal-label-for') + self.lockon_lable = QCheckBox('Enable Lock on Idle (seconds): ') + self.lockon_lable.setToolTip(self.gdm_tooltip['enable_lock_on_idle']) + self.lockon_lable.stateChanged.connect(self.enable_lock_on_idle_changed) self.time_input = QLineEdit() self.time_input.setText(str(self.toml_gdm['lock_on_idle'])) @@ -70,6 +70,7 @@ def init_ui(self): def refresh_config(self, config): self.config = config self.toml_gdm = self.config['gdm'] + self.lockon_lable.setChecked(self.toml_gdm['enable_lock_on_idle']) for name, state in self.toml_gdm.items(): if name == 'lock_on_idle': continue @@ -100,4 +101,12 @@ def time_changed(self, new_size): self.toml_gdm['lock_on_idle'] = int(new_size) else: self.time_input.setText('0') + config_file.write(self.config) + + def enable_lock_on_idle_changed(self, state): + self.toml_gdm['enable_lock_on_idle'] = (state == 2) + if state == 2: + self.time_input.setEnabled(True) + else: + self.time_input.setEnabled(False) config_file.write(self.config) \ No newline at end of file diff --git a/ui/components/software/pam.py b/ui/components/software/pam.py new file mode 100644 index 0000000..a89a44a --- /dev/null +++ b/ui/components/software/pam.py @@ -0,0 +1,148 @@ +from PyQt6.QtWidgets import QWidget, QVBoxLayout, QLabel, QCheckBox \ + , QHBoxLayout, QComboBox, QLineEdit +from harden import config_file +from PyQt6.QtGui import QIntValidator + +class PAM(QWidget): + def __init__(self, config, tooltip): + super().__init__() + self.config = config + self.tooltip = tooltip + self.toml_pam = self.config['pam'] + self.pam_tooltip = self.tooltip['pam'] + self.init_ui() + self.refresh_config(config) + + def init_ui(self): + self.layout = QVBoxLayout() + self.setLayout(self.layout) + self.layout.setSpacing(0) + self.layout.setContentsMargins(0, 0, 0, 0) + + self.main_label = QLabel("PAM") + self.layout.addWidget(self.main_label) + self.main_label.setObjectName("component-title") + + # container widget + self.container_widget = QWidget() + self.container_layout = QVBoxLayout() + self.container_widget.setLayout(self.container_layout) + self.layout.addWidget(self.container_widget) + self.container_layout.setSpacing(0) + self.container_layout.setContentsMargins(30, 30, 30, 30) + self.container_widget.setObjectName("container-widget") + + # Enable Password Checkbox + self.enable_password_checkbox = QCheckBox('Enable Password Level') + self.enable_password_checkbox.setToolTip(self.pam_tooltip['enable_password_level']) + self.enable_password_checkbox.stateChanged.connect(lambda state: self.save_checkbox_state(state, 'enable_password_level')) + self.container_layout.addWidget(self.enable_password_checkbox) + + # Enable Password Dropdown + hlayout = QHBoxLayout() + + # Select Mode Label + self.mode_label = QLabel('Required Password Level:') + self.mode_label.setToolTip(self.pam_tooltip['enable_password_level']) + self.mode_label.setProperty('class', 'normal-label-for') + + # Mode Dropdown + self.mode_list = QComboBox() + self.mode_list.addItems(['weak', 'medium', 'strong', 'stronger']) + self.mode_list.currentTextChanged.connect(lambda text: self.new_item_selected(text, 'required_password_level')) + + hlayout.addWidget(self.mode_label) + hlayout.addWidget(self.mode_list) + self.container_layout.addLayout(hlayout) + + # Enable Password Length Checkbox + self.enable_password_len_checkbox = QCheckBox('Enable Password Length') + self.enable_password_len_checkbox.setToolTip(self.pam_tooltip['enable_password_length']) + self.enable_password_len_checkbox.stateChanged.connect(lambda state: self.save_checkbox_state(state, 'enable_password_length')) + self.container_layout.addWidget(self.enable_password_len_checkbox) + + # Enable Password Dropdown + hlayout = QHBoxLayout() + + self.len_label = QLabel('Minimum Password Length: ') + self.len_label.setToolTip(self.pam_tooltip['enable_password_length']) + self.len_label.setProperty('class', 'normal-label-for') + + self.size_input = QLineEdit() + validator = QIntValidator() + self.size_input.setValidator(validator) + self.size_input.textChanged.connect(lambda text: self.size_changed(text, 'minimum_password_length', self.size_input)) + + hlayout.addWidget(self.len_label) + hlayout.addWidget(self.size_input) + self.container_layout.addLayout(hlayout) + + # Enable Password Length Checkbox + self.limit_password_reuse_checkbox = QCheckBox('Enable Limit Password Reuse') + self.limit_password_reuse_checkbox.setToolTip(self.pam_tooltip['limit_password_reuse']) + self.limit_password_reuse_checkbox.stateChanged.connect(lambda state: self.save_checkbox_state(state, 'limit_password_reuse')) + self.container_layout.addWidget(self.limit_password_reuse_checkbox) + + # Enable Password Dropdown + hlayout = QHBoxLayout() + + self.reuse_label = QLabel('Minimum Password Length: ') + self.reuse_label.setToolTip(self.pam_tooltip['limit_password_reuse']) + self.reuse_label.setProperty('class', 'normal-label-for') + + self.size_input_2 = QLineEdit() + validator = QIntValidator() + self.size_input_2.setValidator(validator) + self.size_input_2.textChanged.connect(lambda text: self.size_changed(text, 'password_reuse_limit', self.size_input_2)) + + hlayout.addWidget(self.reuse_label) + hlayout.addWidget(self.size_input_2) + self.container_layout.addLayout(hlayout) + + # Configure Hashing Algorithm + self.configure_hashing_algorithm = QCheckBox('Configure Hashing Algorithm') + self.configure_hashing_algorithm.setToolTip(self.pam_tooltip['configure_hashing_algorithm']) + self.configure_hashing_algorithm.stateChanged.connect(lambda state: self.save_checkbox_state(state, 'configure_hashing_algorithm')) + self.container_layout.addWidget(self.configure_hashing_algorithm) + + def refresh_config(self, config): + self.config = config + self.toml_pam = self.config['pam'] + self.enable_password_checkbox.setChecked(self.toml_pam['enable_password_level']) + self.enable_password_len_checkbox.setChecked(self.toml_pam['enable_password_length']) + self.limit_password_reuse_checkbox.setChecked(self.toml_pam['limit_password_reuse']) + self.configure_hashing_algorithm.setChecked(self.toml_pam['configure_hashing_algorithm']) + self.mode_list.setCurrentText(self.toml_pam['required_password_level']) + self.size_input.setText(str(self.toml_pam['minimum_password_length'])) + self.size_input_2.setText(str(self.toml_pam['password_reuse_limit'])) + + def save_checkbox_state(self, state, key): + self.toml_pam[key] = (state == 2) + if state == 0: + if key == 'enable_password_level': + self.mode_list.setEnabled(False) + elif key == 'enable_password_length': + self.size_input.setEnabled(False) + elif key == 'limit_password_reuse': + self.size_input_2.setEnabled(False) + else: + if key == 'enable_password_level': + self.mode_list.setEnabled(True) + elif key == 'enable_password_length': + self.size_input.setEnabled(True) + elif key == 'limit_password_reuse': + self.size_input_2.setEnabled(True) + config_file.write(self.config) + + def new_item_selected(self, text, key): + self.toml_pam[key] = text + config_file.write(self.config) + + def size_changed(self, new_size, key, input): + if new_size.startswith('0') and len(new_size) > 1: + input.setText(new_size[1:]) + if new_size: + self.toml_pam[key] = int(new_size) + else: + input.setText('0') + config_file.write(self.config) \ No newline at end of file diff --git a/ui/components/software/time_sync.py b/ui/components/software/time_sync.py index c8a4a6b..97bf41a 100644 --- a/ui/components/software/time_sync.py +++ b/ui/components/software/time_sync.py @@ -42,10 +42,10 @@ def init_ui(self): self.enable_user.stateChanged.connect(lambda state, name = 'enable_ntp_user': self.save_checkbox_state(name, state)) self.container_layout.addWidget(self.enable_user) - ntp_server_lable = QLabel('NTP Servers') - ntp_server_lable.setToolTip(self.time_sync_tooltip['ntp_servers']) - ntp_server_lable.setProperty('class', 'normal-label-for') - self.container_layout.addWidget(ntp_server_lable) + self.ntp_server_checkbox = QCheckBox('Enable NTP Servers') + self.ntp_server_checkbox.setToolTip(self.time_sync_tooltip['enable_ntp_servers']) + self.ntp_server_checkbox.stateChanged.connect(self.enable_ntp_servers_changed) + self.container_layout.addWidget(self.ntp_server_checkbox) hlayout = QHBoxLayout() @@ -54,8 +54,8 @@ def init_ui(self): self.add_button.clicked.connect(self.add_new_server) self.add_button.setProperty('class', 'add-btn') - hlayout.addWidget(self.new_server) hlayout.addWidget(self.add_button) + hlayout.addWidget(self.new_server) self.container_layout.addLayout(hlayout) @@ -121,7 +121,7 @@ def refresh_config(self, config): self.toml_time_sync = self.config['time-sync'] self.enable_ntp.setChecked(self.toml_time_sync['enable_ntp']) self.enable_user.setChecked(self.toml_time_sync['enable_ntp_user']) - + self.ntp_server_checkbox.setChecked(self.toml_time_sync['enable_ntp_servers']) if not self.toml_time_sync['enable_ntp']: self.enable_user.setEnabled(False) self.servers_table.setEnabled(False) @@ -144,3 +144,17 @@ def save_checkbox_state(self, name, state): for i in range(self.servers_table.rowCount()): self.servers_table.cellWidget(i, 1).setEnabled(state == 2) config_file.write(self.config) + + def enable_ntp_servers_changed(self, state): + self.toml_time_sync['enable_ntp_servers'] = (state == 2) + if state == 2: + self.new_server.setEnabled(True) + self.add_button.setEnabled(True) + for i in range(self.servers_table.rowCount()): + self.servers_table.cellWidget(i, 1).setEnabled(True) + else: + self.new_server.setEnabled(False) + self.add_button.setEnabled(False) + for i in range(self.servers_table.rowCount()): + self.servers_table.cellWidget(i, 1).setEnabled(False) + config_file.write(self.config) \ No newline at end of file diff --git a/ui/pages/software_page.py b/ui/pages/software_page.py index 94ae49b..5963579 100644 --- a/ui/pages/software_page.py +++ b/ui/pages/software_page.py @@ -6,6 +6,7 @@ from ui.components.software.services import Services from ui.components.software.service_clients import ServiceClients from ui.components.software.privilege_escalation import PrivilegeEscalation +from ui.components.software.pam import PAM from PyQt6.QtCore import Qt @@ -51,6 +52,10 @@ def init_ui(self): self.privilege_escalation = PrivilegeEscalation(self.config, self.tooltip) self.privilege_escalation.setObjectName("privilege_escalation") + # PAM Widget + self.pam = PAM(self.config, self.tooltip) + self.pam.setObjectName("pam") + self.layout.addWidget(self.process_hardening) self.layout.addWidget(self.apparmor) self.layout.addWidget(self.gdm) @@ -58,6 +63,7 @@ def init_ui(self): self.layout.addWidget(self.services) self.layout.addWidget(self.service_clients) self.layout.addWidget(self.privilege_escalation) + self.layout.addWidget(self.pam) def refresh_config(self, config): self.config = config @@ -67,4 +73,5 @@ def refresh_config(self, config): self.time_sync.refresh_config(config) self.services.refresh_config(config) self.service_clients.refresh_config(config) - self.privilege_escalation.refresh_config(config) \ No newline at end of file + self.privilege_escalation.refresh_config(config) + self.pam.refresh_config(config) \ No newline at end of file diff --git a/ui/qss/light.qss b/ui/qss/light.qss index 7d90f6c..8107d6a 100644 --- a/ui/qss/light.qss +++ b/ui/qss/light.qss @@ -68,7 +68,9 @@ QMainWindow { padding: 2.5px 10px; border-radius: 5px; color: #382F27; - font: 18px; + font: 14px; + margin-right: 10px; + margin-bottom: 5px; font-weight: 500; } .add-btn:hover { @@ -146,7 +148,7 @@ QLineEdit { border-radius: 4px; padding: 2px 5px; color: #382F27; - background-color: #FFF6ED; + background-color: #ffffff; margin: 0px 15px 5px 0px; selection-background-color: #AA8F75; } @@ -175,8 +177,10 @@ QComboBox { border-radius: 4px; padding: 2px 5px; color: #382F27; - background-color: #FFF6ED; + background-color: #ffffff; margin: 0px 15px 5px 0px; + font: 15px; + font-weight: 400; selection-background-color: #AA8F75; }