Skip to content

Commit 987ad24

Browse files
committed
construct jail command arguments with Sysctl
1 parent 60d9419 commit 987ad24

File tree

2 files changed

+165
-92
lines changed

2 files changed

+165
-92
lines changed

libioc/Jail.py

Lines changed: 46 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import shutil
3131

3232
import libzfs
33+
import freebsd_sysctl
3334

3435
import libioc.Types
3536
import libioc.errors
@@ -1653,93 +1654,66 @@ def devfs_ruleset(self) -> libioc.DevfsRules.DevfsRuleset:
16531654
ruleset_line_position = self.host.devfs.index(devfs_ruleset)
16541655
return self.host.devfs[ruleset_line_position].number
16551656

1656-
@property
1657-
def _launch_command(self) -> typing.List[str]:
1658-
1659-
command = ["/usr/sbin/jail", "-c"]
1657+
@staticmethod
1658+
def __get_launch_command(jail_args: typing.List[str]) -> typing.List[str]:
1659+
return ["/usr/sbin/jail", "-c"] + jail_args
16601660

1661-
if self.config["vnet"]:
1662-
command.append("vnet")
1663-
else:
1661+
@property
1662+
def _launch_args(self) -> typing.List[str]:
1663+
config = self.config
1664+
value: str
1665+
jail_param_args: typing.List[str] = []
1666+
for sysctl_name, sysctl in libioc.JailParams.JailParams().items():
1667+
if sysctl.ctl_type == freebsd_sysctl.types.NODE:
1668+
# skip NODE
1669+
continue
16641670

1665-
if self.config["ip4_addr"] is not None:
1666-
ip4_addr = self.config["ip4_addr"]
1667-
command += [
1668-
f"ip4.addr={ip4_addr}",
1669-
f"ip4.saddrsel={self.config['ip4_saddrsel']}",
1670-
f"ip4={self.config['ip4']}",
1671-
]
1671+
if sysctl_name == "security.jail.param.devfs_ruleset":
1672+
value = str(self.devfs_ruleset)
1673+
elif sysctl_name == "security.jail.param.path":
1674+
value = self.root_dataset.mountpoint
1675+
elif sysctl_name == "security.jail.param.name":
1676+
value = self.identifier
1677+
elif sysctl_name == "security.jail.param.allow.mount.zfs":
1678+
value = str(self._allow_mount_zfs)
1679+
elif sysctl_name == "security.jail.param.vnet":
1680+
if config["vnet"] is False:
1681+
# vnet is only used when explicitly enabled
1682+
# (friendly to Kernels without VIMAGE support)
1683+
continue
1684+
value = "vnet"
1685+
else:
1686+
config_property_name = sysctl.iocage_name
1687+
if self.config._is_known_property(config_property_name):
1688+
value = config[config_property_name]
1689+
else:
1690+
continue
16721691

1673-
if self.config['ip6_addr'] is not None:
1674-
ip6_addr = self.config['ip6_addr']
1675-
command += [
1676-
f"ip6.addr={ip6_addr}",
1677-
f"ip6.saddrsel={self.config['ip6_saddrsel']}",
1678-
f"ip6={self.config['ip6']}",
1679-
]
1692+
sysctl.value = value
1693+
jail_param_args.append(str(sysctl))
16801694

1681-
command += [
1682-
f"name={self.identifier}",
1683-
f"host.hostname={self.config['host_hostname']}",
1684-
f"host.domainname={self.config['host_domainname']}",
1685-
f"path={self.root_dataset.mountpoint}",
1686-
f"securelevel={self._get_value('securelevel')}",
1687-
f"host.hostuuid={self.name}",
1688-
f"devfs_ruleset={self.devfs_ruleset}",
1689-
f"enforce_statfs={self._get_value('enforce_statfs')}",
1690-
f"children.max={self._get_value('children_max')}",
1691-
f"allow.set_hostname={self._get_value('allow_set_hostname')}",
1692-
f"allow.sysvipc={self._get_value('allow_sysvipc')}",
1695+
jail_args = [
1696+
f"exec.timeout={self._get_value('exec_timeout')}",
1697+
f"stop.timeout={self._get_value('stop_timeout')}",
16931698
f"exec.prestart=\"{self.get_hook_script_path('prestart')}\"",
16941699
f"exec.prestop=\"{self.get_hook_script_path('prestop')}\"",
16951700
f"exec.poststop=\"{self.get_hook_script_path('poststop')}\"",
1696-
f"exec.jail_user={self._get_value('exec_jail_user')}"
1697-
]
1698-
1699-
if self.host.userland_version > 10.3:
1700-
command += [
1701-
f"sysvmsg={self._get_value('sysvmsg')}",
1702-
f"sysvsem={self._get_value('sysvsem')}",
1703-
f"sysvshm={self._get_value('sysvshm')}"
1704-
]
1705-
1706-
command += [
1707-
f"allow.raw_sockets={self._get_value('allow_raw_sockets')}",
1708-
f"allow.chflags={self._get_value('allow_chflags')}",
1709-
f"allow.mount={self._allow_mount}",
1710-
f"allow.mount.devfs={self._get_value('allow_mount_devfs')}",
1711-
f"allow.mount.nullfs={self._get_value('allow_mount_nullfs')}",
1712-
f"allow.mount.procfs={self._get_value('allow_mount_procfs')}",
1713-
f"allow.mount.fdescfs={self._get_value('allow_mount_fdescfs')}",
1714-
f"allow.mount.zfs={self._allow_mount_zfs}",
1715-
f"allow.quotas={self._get_value('allow_quotas')}",
1716-
f"allow.socket_af={self._get_value('allow_socket_af')}",
1717-
f"exec.timeout={self._get_value('exec_timeout')}",
1718-
f"stop.timeout={self._get_value('stop_timeout')}",
1701+
f"exec.jail_user={self._get_value('exec_jail_user')}",
17191702
f"mount.fstab={self.fstab.path}",
1720-
f"mount.devfs={self._get_value('mount_devfs')}"
1703+
f"mount.devfs={self._get_value('mount_devfs')}",
1704+
"allow.dying"
17211705
]
17221706

1723-
if self.config["allow_vmm"] is True:
1724-
command.append("allow.vmm=1")
1725-
1726-
if self.host.userland_version > 9.3:
1727-
command += [
1728-
f"mount.fdescfs={self._get_value('mount_fdescfs')}",
1729-
f"allow.mount.tmpfs={self._get_value('allow_mount_tmpfs')}"
1730-
]
1731-
1732-
command += ["allow.dying"]
1733-
return command
1707+
return jail_param_args + jail_args
17341708

17351709
def _launch_persistent_jail(
17361710
self,
17371711
passthru: bool
17381712
) -> libioc.helpers.CommandOutput:
1739-
command = self._launch_command + [
1713+
command = self.__get_launch_command(self._launch_args + [
17401714
"persist",
17411715
f"exec.poststart=\"{self.get_hook_script_path('poststart')}\""
1742-
]
1716+
])
17431717

17441718
stdout, stderr, returncode = self._exec_host_command(
17451719
command=command,
@@ -1799,11 +1773,11 @@ def _launch_single_command_jail(
17991773
jail_command: str,
18001774
passthru: bool
18011775
) -> libioc.helpers.CommandOutput:
1802-
command = self._launch_command + [
1776+
command = self.__get_launch_command(self._launch_args + [
18031777
"nopersist",
18041778
f"exec.poststart=\"{self.get_hook_script_path('host_command')}\"",
18051779
"command=/usr/bin/true"
1806-
]
1780+
])
18071781

18081782
_identifier = str(shlex.quote(self.identifier))
18091783
_jls_command = f"/usr/sbin/jls -j {_identifier} jid"

libioc/JailParams.py

Lines changed: 119 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,57 +25,156 @@
2525
"""Sysctl jail params signeton."""
2626
import typing
2727
import freebsd_sysctl
28+
import freebsd_sysctl.types
2829
import collections.abc
30+
import shlex
2931

30-
class HostJailParams(collections.abc.MutableMapping):
31-
__params: typing.Dict[str, freebsd_sysctl.Sysctl]
32+
import libioc.helpers
33+
34+
JailParamValueType = typing.Optional[typing.Union[bool, int, str]]
35+
36+
37+
class JailParam(freebsd_sysctl.Sysctl):
38+
"""Single jail parameter represented by sysctl."""
39+
40+
user_value: JailParamValueType
41+
42+
@property
43+
def value(self) -> JailParamValueType:
44+
"""Return the user defined value of this jail parameter."""
45+
return self.user_value
46+
47+
@value.setter
48+
def value(self, value: JailParamValueType) -> None:
49+
"""Set the user defined value of this jail parameter."""
50+
if self.ctl_type == freebsd_sysctl.types.NODE:
51+
raise TypeError("sysctl NODE has no value")
52+
elif self.ctl_type in [
53+
freebsd_sysctl.types.STRING,
54+
freebsd_sysctl.types.OPAQUE,
55+
]:
56+
if (isinstance(value, int) or isinstance(value, str)) is False:
57+
try:
58+
value = str(value)
59+
except Exception:
60+
self.__raise_value_type_error()
61+
else:
62+
if (isinstance(value, int) or isinstance(value, bool)) is False:
63+
try:
64+
value = int(value) # noqa: T484
65+
except Exception:
66+
self.__raise_value_type_error()
67+
self.user_value = value
68+
69+
@property
70+
def sysctl_value(self) -> JailParamValueType:
71+
"""Return the original freebsd_sysctl.Sysctl value."""
72+
return typing.cast(
73+
JailParamValueType,
74+
super().value
75+
)
76+
77+
def __raise_value_type_error(self) -> None:
78+
type_name = self.ctl_type.__name__
79+
raise TypeError(f"{self.name} sysctl requires {type_name}")
80+
81+
@property
82+
def jail_arg_name(self) -> str:
83+
"""Return the name of the param formatted for the jail command."""
84+
name = str(self.name)
85+
prefix = "security.jail.param."
86+
if name.startswith(prefix) is True:
87+
return name[len(prefix):]
88+
return name
89+
90+
@property
91+
def iocage_name(self) -> str:
92+
"""Return the name of the param formatted for iocage config."""
93+
return self.jail_arg_name.replace(".", "_")
94+
95+
def __str__(self) -> str:
96+
"""Return the jail command argument notation of the param."""
97+
if (self.value is None):
98+
return self.jail_arg_name
99+
100+
if (self.ctl_type == freebsd_sysctl.types.STRING):
101+
escaped_value = shlex.quote(str(self.value))
102+
return f"{self.jail_arg_name}={escaped_value}"
103+
104+
mapped_value = str(libioc.helpers.to_string(
105+
self.value,
106+
true="1",
107+
false="0"
108+
))
109+
return f"{self.jail_arg_name}={mapped_value}"
110+
111+
112+
class JailParams(collections.abc.MutableMapping):
113+
"""Collection of jail parameters."""
114+
115+
__base_class = JailParam
116+
__sysrc_params: typing.Dict[str, freebsd_sysctl.Sysctl]
32117

33118
def __iter__(self) -> typing.Iterator[str]:
34119
"""Iterate over the jail param names."""
35-
yield self.memoized_params.__iter__()
36-
120+
yield from self.memoized_params.__iter__()
121+
37122
def __len__(self) -> int:
38123
"""Return the number of available jail params."""
39124
return self.memoized_params.__len__()
40125

41-
def items(self) -> typing.ItemsView[str, typing.Any]:
126+
def items(self) -> typing.ItemsView[str, freebsd_sysctl.Sysctl]:
42127
"""Iterate over the keys and values."""
43-
return typing.cast(
44-
typing.ItemsView[str, typing.Any],
45-
self.memoized_params.items()
46-
)
128+
return self.memoized_params.items()
47129

48130
def keys(self) -> typing.KeysView[str]:
49131
"""Return a list of all jail param names."""
50-
return collections.abc.KeysView(*list(self.__iter__())) # noqa: T484
132+
return collections.abc.KeysView(list(self.__iter__())) # noqa: T484
51133

52134
def __getitem__(self, key: str) -> typing.Any:
53135
"""Set of jail params sysrc is not implemented."""
54136
return self.memoized_params.__getitem__(key)
55137

56138
def __setitem__(self, key: str, value: typing.Any) -> None:
57139
"""Set of jail params sysrc is not supportes."""
58-
raise NotImplementedError("jail param sysctl cannot be modified")
140+
self.memoized_params.__setitem__(key, value)
59141

60-
def __delitem__(self, key: str, value: typing.Any) -> None:
142+
def __delitem__(self, key: str) -> None:
61143
"""Delete of jail param sysrc not supported."""
62-
raise NotImplementedError("jail param sysctl cannot be deleted")
144+
self.memoized_params.__delitem__(key)
63145

64146
@property
65147
def memoized_params(self) -> typing.Dict[str, freebsd_sysctl.Sysctl]:
148+
"""Return the memorized params initialized on first access."""
66149
try:
67-
return self.__params
150+
return self.__sysrc_params
68151
except AttributeError:
69152
pass
70153
self.__update_sysrc_jail_params()
71-
return self.__params
154+
return self.__sysrc_params
72155

73156
def __update_sysrc_jail_params(self) -> None:
74157
prefix = "security.jail.param"
75158
jail_params = filter(
76-
lambda x: x.name.endswith(".") is False, # filter NODE
77-
freebsd_sysctl.Sysctl(prefix).children
78-
)
79-
HostJailParams.__params = dict(
80-
[(x.name[len(prefix) + 1:], x,) for x in jail_params]
159+
lambda x: not any((
160+
x.name.endswith("."), # quick filter NODE
161+
x.name == "security.jail.allow_raw_sockets", # deprecated
162+
)),
163+
self.__base_class(prefix).children
81164
)
165+
# permanently store the queried sysrc in the singleton class
166+
JailParams.__sysrc_params = dict([(x.name, x,) for x in jail_params])
167+
168+
169+
class HostJailParams(JailParams):
170+
"""Read-only host jail parameters obtained from sysrc."""
171+
172+
__base_class = freebsd_sysctl.Sysctl
173+
174+
def __setitem__(self, key: str, value: typing.Any) -> None:
175+
"""Set of jail params sysrc is not supportes."""
176+
raise NotImplementedError("jail param sysctl cannot be modified")
177+
178+
def __delitem__(self, key: str) -> None:
179+
"""Delete of jail param sysrc not supported."""
180+
raise NotImplementedError("jail param sysctl cannot be deleted")

0 commit comments

Comments
 (0)