This commit is contained in:
Cooper 2026-08-13 11:30:58 +00:00 committed by GitHub
commit a576076eec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 65 additions and 5 deletions

View File

@ -32,6 +32,24 @@ class FirewallApp:
'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 @@ class FirewallApp:
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)

View File

@ -76,6 +76,7 @@ class ApplicationMenu(AbstractSubMenu[ApplicationConfiguration]):
MenuItem(
text=tr('Firewall'),
action=select_firewall,
value=self._app_config.firewall_config,
preview_action=self._prev_firewall,
key='firewall_config',
),
@ -121,7 +122,10 @@ class ApplicationMenu(AbstractSubMenu[ApplicationConfiguration]):
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 output
return None
def _prev_fonts(self, item: MenuItem) -> str | None:
@ -230,7 +234,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

View File

@ -364,6 +364,8 @@ class GlobalMenu(AbstractMenu[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 @@ class GlobalMenu(AbstractMenu[None]):
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:

View File

@ -40,6 +40,7 @@ class Firewall(StrEnum):
class FirewallConfigSerialization(TypedDict):
firewall: str
allow_ssh: NotRequired[bool]
class FontPackage(StrEnum):
@ -143,16 +144,16 @@ class PrintServiceConfiguration:
@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,10 @@ class ApplicationConfiguration(SubConfig):
if self.firewall_config:
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)