Skip to content

Commit e0af4db

Browse files
committed
Add support for systemd-homed
1 parent d40d77c commit e0af4db

File tree

4 files changed

+111
-20
lines changed

4 files changed

+111
-20
lines changed

archinstall/lib/disk/device_handler.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -271,12 +271,15 @@ def format(
271271
options = []
272272

273273
match fs_type:
274-
case FilesystemType.Btrfs | FilesystemType.F2fs | FilesystemType.Xfs:
274+
case FilesystemType.Btrfs | FilesystemType.Xfs:
275275
# Force overwrite
276276
options.append('-f')
277+
case FilesystemType.F2fs:
278+
# Force overwrite and enable encrypt
279+
options.extend(('-f', '-O', 'encrypt'))
277280
case FilesystemType.Ext2 | FilesystemType.Ext3 | FilesystemType.Ext4:
278-
# Force create
279-
options.append('-F')
281+
# Force create and enable encrypt
282+
options.extend(('-F', '-O', 'encrypt'))
280283
case FilesystemType.Fat12 | FilesystemType.Fat16 | FilesystemType.Fat32:
281284
mkfs_type = 'fat'
282285
# Set FAT size

archinstall/lib/installer.py

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
from .args import arch_config_handler
3838
from .exceptions import DiskError, HardwareIncompatibilityError, RequirementError, ServiceException, SysCallError
39-
from .general import SysCommand, run
39+
from .general import SysCommand, SysCommandWorker, run
4040
from .hardware import SysInfo
4141
from .locale.utils import verify_keyboard_layout, verify_x11_keyboard_layout
4242
from .luks import Luks2
@@ -1701,27 +1701,48 @@ def _create_user(self, user: User) -> None:
17011701
if not handled_by_plugin:
17021702
info(f'Creating user {user.username}')
17031703

1704-
cmd = f'arch-chroot {self.target} useradd -m'
1704+
if user.homed is not None:
1705+
self.enable_service('systemd-homed')
17051706

1706-
if user.sudo:
1707-
cmd += ' -G wheel'
1707+
homed_groups = []
1708+
if user.sudo:
1709+
homed_groups.append('wheel')
1710+
homed_groups.extend(user.groups)
17081711

1709-
cmd += f' {user.username}'
1712+
cmd = f'homectl create --enforce-password-policy=no'
1713+
cmd += f' --member-of {",".join(homed_groups)}' if homed_groups else ''
1714+
cmd += f' --storage {user.homed.storage_mechanism}'
1715+
cmd += f' {user.username}'
17101716

1711-
try:
1712-
SysCommand(cmd)
1713-
except SysCallError as err:
1714-
raise SystemError(f'Could not create user inside installation: {err}')
1717+
from .boot import Boot
1718+
1719+
with Boot(self) as session:
1720+
worker = session.SysCommandWorker(cmd.split(" "))
1721+
while worker.is_alive():
1722+
worker.write(bytes(f'{user.password.plaintext}\n', 'UTF-8'), line_ending=False)
1723+
else:
1724+
cmd = f'arch-chroot {self.target} useradd -m'
1725+
1726+
if user.sudo:
1727+
cmd += ' -G wheel'
1728+
1729+
cmd += f' {user.username}'
1730+
1731+
try:
1732+
SysCommand(cmd)
1733+
except SysCallError as err:
1734+
raise SystemError(f'Could not create user inside installation: {err}')
17151735

17161736
for plugin in plugins.values():
17171737
if hasattr(plugin, 'on_user_created'):
17181738
if result := plugin.on_user_created(self, user):
17191739
handled_by_plugin = result
17201740

1721-
self.set_user_password(user)
1741+
if user.homed is None:
1742+
self.set_user_password(user)
17221743

1723-
for group in user.groups:
1724-
SysCommand(f'arch-chroot {self.target} gpasswd -a {user.username} {group}')
1744+
for group in user.groups:
1745+
SysCommand(f'arch-chroot {self.target} gpasswd -a {user.username} {group}')
17251746

17261747
if user.sudo:
17271748
self.enable_sudo(user)

archinstall/lib/interactions/manage_users_conf.py

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
from archinstall.tui.curses_menu import EditMenu, SelectMenu
88
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
99
from archinstall.tui.result import ResultType
10-
from archinstall.tui.types import Alignment, Orientation
10+
from archinstall.tui.types import Alignment, FrameProperties, Orientation
1111

1212
from ..menu.list_manager import ListManager
13-
from ..models.users import User
13+
from ..models.users import User, StorageMechanism, HomedConfiguration
1414
from ..utils.util import get_password
1515

1616

@@ -111,8 +111,55 @@ def _add_user(self) -> User | None:
111111
case _:
112112
raise ValueError('Unhandled result type')
113113

114-
return User(username, password, sudo)
114+
homed_configuration = self._configure_homed()
115115

116+
return User(username, password, sudo, homed_configuration)
117+
118+
119+
def _configure_homed(self) -> HomedConfiguration | None:
120+
header = str(tr('Should the user use systemd-homed?\n'))
121+
122+
group = MenuItemGroup.yes_no()
123+
group.focus_item = MenuItem.no()
124+
125+
result = SelectMenu[bool](
126+
group,
127+
header=header,
128+
alignment=Alignment.CENTER,
129+
columns=2,
130+
orientation=Orientation.HORIZONTAL,
131+
search_enabled=False,
132+
allow_skip=False,
133+
).run()
134+
135+
match result.type_:
136+
case ResultType.Selection:
137+
if result.item() != MenuItem.yes():
138+
return None
139+
case _:
140+
raise ValueError('Unhandled result type')
141+
142+
items = [
143+
MenuItem('Directory', value=StorageMechanism.DIRECTORY),
144+
MenuItem('LUKS', value=StorageMechanism.LUKS),
145+
MenuItem('fscrypt', value=StorageMechanism.FSCRYPT),
146+
]
147+
148+
group = MenuItemGroup(items, sort_items=False)
149+
result = SelectMenu[StorageMechanism](
150+
group,
151+
alignment=Alignment.CENTER,
152+
frame=FrameProperties.min('Filesystem'),
153+
allow_skip=False,
154+
).run()
155+
156+
match result.type_:
157+
case ResultType.Selection:
158+
return HomedConfiguration(result.get_value())
159+
case _:
160+
raise ValueError('Unhandled result type')
161+
162+
return None
116163

117164
def ask_for_additional_users(prompt: str = '', defined_users: list[User] = []) -> list[User]:
118165
users = UserList(prompt, defined_users).run()

archinstall/lib/models/users.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,28 @@ def _check_password_strength(
100100
return PasswordStrength.VERY_WEAK
101101

102102

103+
104+
class StorageMechanism:
105+
DIRECTORY = 'directory'
106+
LUKS = 'luks'
107+
FSCRYPT = 'fscrypt'
108+
109+
110+
class HomedConfiguration:
111+
def __init__(
112+
self,
113+
storage_mechanism: StorageMechanism,
114+
):
115+
self.storage_mechanism = storage_mechanism
116+
117+
103118
UserSerialization = TypedDict(
104119
'UserSerialization',
105120
{
106121
'username': str,
107122
'!password': NotRequired[str],
108123
'sudo': bool,
124+
'homed': HomedConfiguration | None,
109125
'groups': list[str],
110126
'enc_password': str | None,
111127
},
@@ -158,18 +174,20 @@ class User:
158174
username: str
159175
password: Password
160176
sudo: bool
177+
homed: HomedConfiguration | None = None
161178
groups: list[str] = field(default_factory=list)
162179

163180
@override
164181
def __str__(self) -> str:
165182
# safety overwrite to make sure password is not leaked
166-
return f'User({self.username=}, {self.sudo=}, {self.groups=})'
183+
return f'User({self.username=}, {self.sudo=}, {self.homed=}, {self.groups=})'
167184

168-
def table_data(self) -> dict[str, str | bool | list[str]]:
185+
def table_data(self) -> dict[str, str | bool | StorageMechanism | list[str]]:
169186
return {
170187
'username': self.username,
171188
'password': self.password.hidden(),
172189
'sudo': self.sudo,
190+
'homed': self.homed.storage_mechanism if self.homed else False,
173191
'groups': self.groups,
174192
}
175193

@@ -178,6 +196,7 @@ def json(self) -> UserSerialization:
178196
'username': self.username,
179197
'enc_password': self.password.enc_password,
180198
'sudo': self.sudo,
199+
'homed': self.homed,
181200
'groups': self.groups,
182201
}
183202

@@ -208,6 +227,7 @@ def parse_arguments(
208227
username=username,
209228
password=password,
210229
sudo=entry.get('sudo', False) is True,
230+
homed=entry.get('homed', None),
211231
groups=groups,
212232
)
213233

0 commit comments

Comments
 (0)