From 113988a6aae3be07e6f546823f6b5db4bb19d2cd Mon Sep 17 00:00:00 2001 From: CooperWang0912 Date: Tue, 28 Jul 2026 13:04:13 +0800 Subject: [PATCH 1/8] Initial Testing --- archinstall/lib/configuration.py | 17 +++++++++++++++++ archinstall/scripts/guided.py | 3 ++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/archinstall/lib/configuration.py b/archinstall/lib/configuration.py index cb6844c3cd..366a52bcae 100644 --- a/archinstall/lib/configuration.py +++ b/archinstall/lib/configuration.py @@ -8,6 +8,23 @@ from archinstall.tui.menu_item import MenuItem, MenuItemGroup from archinstall.tui.result import ResultType +async def confirm_ufw(config: ArchConfig) -> None: + firewall_config = ArchConfig.app_config.firewall_config + is_ufw = firewall_config and firewall_config.firewall and firewall_config.firewall.value == 'ufw' + if is_ufw and "openssh" in ArchConfig.packages: + header = f'{tr("You have both ufw and OpenSSH in your packages")}. ' + header += tr('Would you like to allow incoming SSH connections through the firewall?') + '\n' + group = MenuItemGroup.yes_no() + + result = await Confirmation( + group=group, + header=header, + allow_skip=False, + preset=True, + ).show() + + if result: + ArchConfig.custom_commands.append("ufw allow OpenSSH") async def confirm_config(config: ArchConfig) -> bool: header = f'{tr("The specified configuration will be applied")}. ' diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 627a3b7553..b7406b79b2 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -6,7 +6,7 @@ from archinstall.lib.args import ArchConfig, ArchConfigHandler from archinstall.lib.authentication.authentication_handler import AuthenticationHandler from archinstall.lib.bootloader.utils import validate_bootloader_layout -from archinstall.lib.configuration import confirm_config +from archinstall.lib.configuration import confirm_ufw, confirm_config from archinstall.lib.disk.filesystem import FilesystemHandler from archinstall.lib.disk.utils import disk_layouts from archinstall.lib.general.general_menu import PostInstallationAction, select_post_installation @@ -230,6 +230,7 @@ def main(arch_config_handler: ArchConfigHandler | None = None) -> None: if not arch_config_handler.args.silent: aborted = False + tui.run(lambda: confirm_ufw(arch_config_handler.config)) res: bool = tui.run(lambda: confirm_config(arch_config_handler.config)) if not res: From 969663b8bfc224d3c99a2f177c4d6cdcccf3cbe8 Mon Sep 17 00:00:00 2001 From: CooperWang0912 Date: Tue, 28 Jul 2026 13:08:43 +0800 Subject: [PATCH 2/8] Fixed Value Access --- archinstall/lib/configuration.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/archinstall/lib/configuration.py b/archinstall/lib/configuration.py index 366a52bcae..f96486b06b 100644 --- a/archinstall/lib/configuration.py +++ b/archinstall/lib/configuration.py @@ -9,9 +9,9 @@ from archinstall.tui.result import ResultType async def confirm_ufw(config: ArchConfig) -> None: - firewall_config = ArchConfig.app_config.firewall_config + firewall_config = config.app_config.firewall_config is_ufw = firewall_config and firewall_config.firewall and firewall_config.firewall.value == 'ufw' - if is_ufw and "openssh" in ArchConfig.packages: + if is_ufw and "openssh" in config.packages: header = f'{tr("You have both ufw and OpenSSH in your packages")}. ' header += tr('Would you like to allow incoming SSH connections through the firewall?') + '\n' group = MenuItemGroup.yes_no() @@ -24,7 +24,7 @@ async def confirm_ufw(config: ArchConfig) -> None: ).show() if result: - ArchConfig.custom_commands.append("ufw allow OpenSSH") + config.custom_commands.append("ufw allow OpenSSH") async def confirm_config(config: ArchConfig) -> bool: header = f'{tr("The specified configuration will be applied")}. ' From 5178fa54c1a8aec53d2d3f10553e08be1813009f Mon Sep 17 00:00:00 2001 From: CooperWang0912 Date: Tue, 28 Jul 2026 13:53:23 +0800 Subject: [PATCH 3/8] Avoid None Return --- archinstall/lib/configuration.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/archinstall/lib/configuration.py b/archinstall/lib/configuration.py index f96486b06b..39d0fa9868 100644 --- a/archinstall/lib/configuration.py +++ b/archinstall/lib/configuration.py @@ -8,10 +8,15 @@ from archinstall.tui.menu_item import MenuItem, MenuItemGroup from archinstall.tui.result import ResultType -async def confirm_ufw(config: ArchConfig) -> None: +async def confirm_ufw(config: ArchConfig) -> bool: firewall_config = config.app_config.firewall_config is_ufw = firewall_config and firewall_config.firewall and firewall_config.firewall.value == 'ufw' - if is_ufw and "openssh" in config.packages: + has_openssh = "openssh" in config.packages + + if not (is_ufw and has_openssh): + return True + + else: header = f'{tr("You have both ufw and OpenSSH in your packages")}. ' header += tr('Would you like to allow incoming SSH connections through the firewall?') + '\n' group = MenuItemGroup.yes_no() @@ -23,9 +28,11 @@ async def confirm_ufw(config: ArchConfig) -> None: preset=True, ).show() - if result: + if result and result.get_value(): config.custom_commands.append("ufw allow OpenSSH") + return True + async def confirm_config(config: ArchConfig) -> bool: header = f'{tr("The specified configuration will be applied")}. ' header += tr('Would you like to continue?') + '\n' From ebdc3f1a7a0e954642af59686dd5f2327028babb Mon Sep 17 00:00:00 2001 From: CooperWang0912 Date: Tue, 28 Jul 2026 14:00:02 +0800 Subject: [PATCH 4/8] Debug and Formatting --- archinstall/lib/configuration.py | 10 ++++++---- archinstall/scripts/guided.py | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/archinstall/lib/configuration.py b/archinstall/lib/configuration.py index 39d0fa9868..29dabcced0 100644 --- a/archinstall/lib/configuration.py +++ b/archinstall/lib/configuration.py @@ -8,14 +8,15 @@ from archinstall.tui.menu_item import MenuItem, MenuItemGroup from archinstall.tui.result import ResultType + async def confirm_ufw(config: ArchConfig) -> bool: firewall_config = config.app_config.firewall_config is_ufw = firewall_config and firewall_config.firewall and firewall_config.firewall.value == 'ufw' - has_openssh = "openssh" in config.packages + has_openssh = 'openssh' in config.packages if not (is_ufw and has_openssh): return True - + else: header = f'{tr("You have both ufw and OpenSSH in your packages")}. ' header += tr('Would you like to allow incoming SSH connections through the firewall?') + '\n' @@ -27,12 +28,13 @@ async def confirm_ufw(config: ArchConfig) -> bool: allow_skip=False, preset=True, ).show() - + if result and result.get_value(): - config.custom_commands.append("ufw allow OpenSSH") + config.custom_commands.append('ufw allow SSH') return True + async def confirm_config(config: ArchConfig) -> bool: header = f'{tr("The specified configuration will be applied")}. ' header += tr('Would you like to continue?') + '\n' diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index b7406b79b2..9826d53b39 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -6,7 +6,7 @@ from archinstall.lib.args import ArchConfig, ArchConfigHandler from archinstall.lib.authentication.authentication_handler import AuthenticationHandler from archinstall.lib.bootloader.utils import validate_bootloader_layout -from archinstall.lib.configuration import confirm_ufw, confirm_config +from archinstall.lib.configuration import confirm_config, confirm_ufw from archinstall.lib.disk.filesystem import FilesystemHandler from archinstall.lib.disk.utils import disk_layouts from archinstall.lib.general.general_menu import PostInstallationAction, select_post_installation From ff321b63d1d3ef05318172196aea3684240a1612 Mon Sep 17 00:00:00 2001 From: CooperWang0912 Date: Tue, 28 Jul 2026 14:41:48 +0800 Subject: [PATCH 5/8] Refactored Command Execution --- archinstall/applications/firewall.py | 21 +++++++++++++++++++++ archinstall/lib/configuration.py | 2 +- archinstall/lib/models/application.py | 9 ++++++--- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/archinstall/applications/firewall.py b/archinstall/applications/firewall.py index dadaa05b16..c7be6f2afc 100644 --- a/archinstall/applications/firewall.py +++ b/archinstall/applications/firewall.py @@ -32,6 +32,24 @@ def fwd_services(self) -> list[str]: 'firewalld.service', ] + def _allow_ufw_ssh_on_first_boot(self, install_session: Installer) -> None: + service_content = """[Unit] + Description=Allow SSH in UFW on first boot + After=ufw.service + Wants=ufw.service + + [Service] + Type=oneshot + ExecStart=/usr/bin/ufw allow SSH + ExecStartPost=/usr/bin/systemctl disable ufw-allow-ssh.service + + [Install] + WantedBy=multi-user.target + """ + service_path = install_session.target / 'etc/systemd/system/ufw-allow-ssh.service' + service_path.write_text(service_content) + install_session.enable_service(['ufw-allow-ssh.service']) + def install( self, install_session: Installer, @@ -47,6 +65,9 @@ def install( ufw_conf = install_session.target / 'etc/ufw/ufw.conf' ufw_conf.write_text(ufw_conf.read_text().replace('ENABLED=no', 'ENABLED=yes')) + if firewall_config.allow_ssh: + self._allow_ufw_ssh_on_first_boot(install_session) + case Firewall.FWD: install_session.add_additional_packages(self.fwd_packages) install_session.enable_service(self.fwd_services) diff --git a/archinstall/lib/configuration.py b/archinstall/lib/configuration.py index 29dabcced0..c4d85ed13b 100644 --- a/archinstall/lib/configuration.py +++ b/archinstall/lib/configuration.py @@ -30,7 +30,7 @@ async def confirm_ufw(config: ArchConfig) -> bool: ).show() if result and result.get_value(): - config.custom_commands.append('ufw allow SSH') + config.app_config.firewall_config.allow_ssh = True return True diff --git a/archinstall/lib/models/application.py b/archinstall/lib/models/application.py index 1e97a7846b..396a10a99d 100644 --- a/archinstall/lib/models/application.py +++ b/archinstall/lib/models/application.py @@ -40,6 +40,7 @@ class Firewall(StrEnum): class FirewallConfigSerialization(TypedDict): firewall: str + allow_ssh: NotRequired[bool] class FontPackage(StrEnum): @@ -143,16 +144,16 @@ def parse_arg(cls, arg: PrintServiceConfigSerialization) -> Self: @dataclass class FirewallConfiguration: firewall: Firewall + allow_ssh: bool = False def json(self) -> FirewallConfigSerialization: - return { - 'firewall': self.firewall.value, - } + return {'firewall': self.firewall.value, 'allow_ssh': self.allow_ssh} @classmethod def parse_arg(cls, arg: dict[str, Any]) -> Self: return cls( Firewall(arg['firewall']), + allow_ssh=arg.get('allow_ssh', False), ) @@ -285,6 +286,8 @@ def summary(self) -> list[str]: if self.firewall_config: out.append(tr('Firewall "{}"').format(self.firewall_config.firewall)) + if self.firewall_config.allow_ssh: + out.append({tr('SSH allowed')}) if self.fonts_config and self.fonts_config.fonts: fonts = ', '.join(f.value for f in self.fonts_config.fonts) From fd50d54a7072e76b2e4ea80f8d7e9dee45920c1f Mon Sep 17 00:00:00 2001 From: CooperWang0912 Date: Wed, 29 Jul 2026 17:20:24 +0800 Subject: [PATCH 6/8] Refactored to Selection Menu and Warning --- .../lib/applications/application_menu.py | 28 +++++++++++++++++-- archinstall/lib/configuration.py | 26 ----------------- archinstall/lib/global_menu.py | 9 ++++++ archinstall/lib/models/application.py | 2 +- archinstall/scripts/guided.py | 3 +- 5 files changed, 37 insertions(+), 31 deletions(-) diff --git a/archinstall/lib/applications/application_menu.py b/archinstall/lib/applications/application_menu.py index 99e4872692..47be1f05e8 100644 --- a/archinstall/lib/applications/application_menu.py +++ b/archinstall/lib/applications/application_menu.py @@ -76,6 +76,7 @@ def _define_menu_options(self) -> list[MenuItem]: MenuItem( text=tr('Firewall'), action=select_firewall, + value=self._app_config.firewall_config, preview_action=self._prev_firewall, key='firewall_config', ), @@ -121,7 +122,9 @@ def _prev_print_service(self, item: MenuItem) -> str | None: def _prev_firewall(self, item: MenuItem) -> str | None: if item.value is not None: config: FirewallConfiguration = item.value - return f'{tr("Firewall")}: {config.firewall.value}' + output = f'{tr("Firewall")}: {config.firewall.value}' + output += '\n' + output += f'{tr("Allow SSH")}: {config.allow_ssh}' return None def _prev_fonts(self, item: MenuItem) -> str | None: @@ -230,7 +233,28 @@ async def select_firewall(preset: FirewallConfiguration | None = None) -> Firewa case ResultType.Skip: return preset case ResultType.Selection: - return FirewallConfiguration(firewall=result.get_value()) + selected_firewall = result.get_value() + header = tr('Would you like to allow incoming SSH connections through the firewall?') + '\n' + preset_ssh = preset.allow_ssh if preset else False + + ssh_result = await Confirmation( + header=header, + allow_skip=True, + preset=preset_ssh, + ).show() + + match ssh_result.type_: + case ResultType.Skip: + allow_ssh = preset_ssh + case ResultType.Selection: + allow_ssh = ssh_result.get_value() + case ResultType.Reset: + allow_ssh = False + + return FirewallConfiguration( + firewall=selected_firewall, + allow_ssh=allow_ssh, + ) case ResultType.Reset: return None diff --git a/archinstall/lib/configuration.py b/archinstall/lib/configuration.py index c4d85ed13b..cb6844c3cd 100644 --- a/archinstall/lib/configuration.py +++ b/archinstall/lib/configuration.py @@ -9,32 +9,6 @@ from archinstall.tui.result import ResultType -async def confirm_ufw(config: ArchConfig) -> bool: - firewall_config = config.app_config.firewall_config - is_ufw = firewall_config and firewall_config.firewall and firewall_config.firewall.value == 'ufw' - has_openssh = 'openssh' in config.packages - - if not (is_ufw and has_openssh): - return True - - else: - header = f'{tr("You have both ufw and OpenSSH in your packages")}. ' - header += tr('Would you like to allow incoming SSH connections through the firewall?') + '\n' - group = MenuItemGroup.yes_no() - - result = await Confirmation( - group=group, - header=header, - allow_skip=False, - preset=True, - ).show() - - if result and result.get_value(): - config.app_config.firewall_config.allow_ssh = True - - return True - - async def confirm_config(config: ArchConfig) -> bool: header = f'{tr("The specified configuration will be applied")}. ' header += tr('Would you like to continue?') + '\n' diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index 72c31033af..3c582978fc 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -364,6 +364,8 @@ def _prev_applications(self, item: MenuItem) -> str | None: firewall_config = app_config.firewall_config output += f'{tr("Firewall")}: {firewall_config.firewall.value}' output += '\n' + output += f'{tr("Allow SSH")}: {firewall_config.allow_ssh}' + output += '\n' return output @@ -500,6 +502,13 @@ def _get_install_warnings(self) -> list[str]: if not isinstance(self._arch_config.network_config, NetworkConfiguration): warnings.append(tr('No network configuration selected. Network will need to be set up manually on the installed system.')) + firewall_config = self._arch_config.app_config.firewall_config + is_ufw = firewall_config and firewall_config.firewall and firewall_config.firewall.value == 'ufw' + has_openssh = 'openssh' in self._arch_config.packages + + if is_ufw and has_openssh and not firewall_config.allow_ssh: + warnings.append(tr('SSH not allowed through ufw. Rules will need to be set up manually on the installed system.')) + return warnings def _prev_install_invalid_config(self, item: MenuItem) -> PreviewResult | None: diff --git a/archinstall/lib/models/application.py b/archinstall/lib/models/application.py index 396a10a99d..b32ca60856 100644 --- a/archinstall/lib/models/application.py +++ b/archinstall/lib/models/application.py @@ -287,7 +287,7 @@ def summary(self) -> list[str]: if self.firewall_config: out.append(tr('Firewall "{}"').format(self.firewall_config.firewall)) if self.firewall_config.allow_ssh: - out.append({tr('SSH allowed')}) + out.append(tr('SSH allowed')) if self.fonts_config and self.fonts_config.fonts: fonts = ', '.join(f.value for f in self.fonts_config.fonts) diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 9826d53b39..627a3b7553 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -6,7 +6,7 @@ from archinstall.lib.args import ArchConfig, ArchConfigHandler from archinstall.lib.authentication.authentication_handler import AuthenticationHandler from archinstall.lib.bootloader.utils import validate_bootloader_layout -from archinstall.lib.configuration import confirm_config, confirm_ufw +from archinstall.lib.configuration import confirm_config from archinstall.lib.disk.filesystem import FilesystemHandler from archinstall.lib.disk.utils import disk_layouts from archinstall.lib.general.general_menu import PostInstallationAction, select_post_installation @@ -230,7 +230,6 @@ def main(arch_config_handler: ArchConfigHandler | None = None) -> None: if not arch_config_handler.args.silent: aborted = False - tui.run(lambda: confirm_ufw(arch_config_handler.config)) res: bool = tui.run(lambda: confirm_config(arch_config_handler.config)) if not res: From 4e8bc434146336457fd907e7f2ff42170be8b014 Mon Sep 17 00:00:00 2001 From: CooperWang0912 Date: Wed, 29 Jul 2026 17:24:17 +0800 Subject: [PATCH 7/8] Fix Missing Return --- archinstall/lib/applications/application_menu.py | 1 + 1 file changed, 1 insertion(+) diff --git a/archinstall/lib/applications/application_menu.py b/archinstall/lib/applications/application_menu.py index 47be1f05e8..f1bf4a3ac3 100644 --- a/archinstall/lib/applications/application_menu.py +++ b/archinstall/lib/applications/application_menu.py @@ -125,6 +125,7 @@ def _prev_firewall(self, item: MenuItem) -> str | None: output = f'{tr("Firewall")}: {config.firewall.value}' output += '\n' output += f'{tr("Allow SSH")}: {config.allow_ssh}' + return output return None def _prev_fonts(self, item: MenuItem) -> str | None: From 4edbd78e471caf347208ec88c279658247a28580 Mon Sep 17 00:00:00 2001 From: CooperWang0912 Date: Wed, 29 Jul 2026 18:47:22 +0800 Subject: [PATCH 8/8] Update application.py --- archinstall/lib/models/application.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/archinstall/lib/models/application.py b/archinstall/lib/models/application.py index b32ca60856..1b39c98d23 100644 --- a/archinstall/lib/models/application.py +++ b/archinstall/lib/models/application.py @@ -288,6 +288,8 @@ def summary(self) -> list[str]: out.append(tr('Firewall "{}"').format(self.firewall_config.firewall)) if self.firewall_config.allow_ssh: out.append(tr('SSH allowed')) + else: + out.append(tr('SSH not allowed')) if self.fonts_config and self.fonts_config.fonts: fonts = ', '.join(f.value for f in self.fonts_config.fonts)