Skip to content

Obtain jail parameters from sysctl #646

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Feb 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ JAIL_NET?=16
MYPYPATH = $(shell pwd)/.travis/mypy-stubs

deps:
if [ "`uname`" = "FreeBSD" ]; then pkg install -q -y libucl py36-cython rsync python36 py36-libzfs py36-sysctl; fi
if [ "`uname`" = "FreeBSD" ]; then pkg install -q -y libucl py36-cython rsync python36 py36-libzfs; fi
python3.6 -m ensurepip
python3.6 -m pip install -Ur requirements.txt
install: deps
Expand Down
6 changes: 6 additions & 0 deletions libioc/Config/Jail/BaseConfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import libioc.errors
import libioc.helpers
import libioc.helpers_object
import libioc.JailParams

# mypy
import libioc.Logger
Expand Down Expand Up @@ -815,6 +816,8 @@ def _is_user_property(self, key: str) -> bool:

def _is_known_property(self, key: str) -> bool:
"""Return True when the key is a known config property."""
if self._is_known_jail_param(key):
return True
if key in libioc.Config.Jail.Defaults.DEFAULTS.keys():
return True # key is default
if f"_set_{key}" in dict.__dir__(self):
Expand All @@ -827,6 +830,9 @@ def _is_known_property(self, key: str) -> bool:
return True # user.* property
return False

def _is_known_jail_param(self, key: str) -> bool:
return key in libioc.JailParams.HostJailParams()

@property
def _sorted_user_properties(self) -> typing.List[str]:
return sorted(self.keys())
Expand Down
2 changes: 1 addition & 1 deletion libioc/Config/Jail/Defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
"mount_procfs": "0",
"mount_devfs": "1",
"mount_fdescfs": "0",
"securelevel": "2",
"securelevel": 2,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how come there's no change to the others?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mount_* BaseConfig items relate to usr.sbin/jail arguments, not to jail params. Handling of them remains unchanged, so that a change is not required in terms of introducing py-freebsd_sysctl

"tags": [],
"template": False,
"jail_zfs": False,
Expand Down
33 changes: 24 additions & 9 deletions libioc/Firewall.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import typing
import shlex

import sysctl
import freebsd_sysctl

import libioc.helpers
import libioc.helpers_object
Expand Down Expand Up @@ -58,15 +58,30 @@ def _required_sysctl_properties(self) -> typing.Dict[str, int]:
def ensure_firewall_enabled(self) -> None:
"""Raise an FirewallDisabled exception if the firewall is disabled."""
requirements = self._required_sysctl_properties
requirement_keys = list(requirements.keys())
for item in sysctl.filter("net"):
if item.name in requirement_keys:
if item.value != requirements[item.name]:
state = ("en" if (item.value == 0) else "dis") + "abled"
raise libioc.errors.FirewallDisabled(
hint=f"sysctl {item.name} is not {state}",
logger=self.logger

if len(requirements) == 0:
return

try:
current = "not found"
for key in requirements:
expected = requirements[key]
current = freebsd_sysctl.Sysctl(key).value
if current == expected:
raise ValueError(
f"Invalid Sysctl {key}: "
f"{current} found, but expected: {expected}"
)
return
except Exception:
# an IocageException is raised in the next step at the right level
pass

hint = f"sysctl {key} is expected to be {expected}, but was {current}"
raise libioc.errors.FirewallDisabled(
hint=hint,
logger=self.logger
)

def delete_rule(
self,
Expand Down
33 changes: 20 additions & 13 deletions libioc/Host.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import os
import platform
import re
import sysctl
import freebsd_sysctl

import libzfs

Expand All @@ -54,7 +54,7 @@ class HostGenerator:

_devfs: libioc.DevfsRules.DevfsRules
_defaults: libioc.Resource.DefaultResource
_defaults_initialized = False
__user_provided_defaults: typing.Optional[libioc.Resource.DefaultResource]
__hostid: str
releases_dataset: libzfs.ZFSDataset
datasets: libioc.Datasets.Datasets
Expand Down Expand Up @@ -99,13 +99,12 @@ def __init__(
zfs=self.zfs
)

self._init_defaults(defaults)

def _init_defaults(
self,
defaults: typing.Optional[libioc.Resource.DefaultResource]=None
) -> None:
# this variable stores user provided defaults until the property
# was initialized and accessed the first time
self.__user_provided_defaults = defaults

def __init_defaults(self) -> None:
defaults = self.__user_provided_defaults
if defaults is not None:
self._defaults = defaults
else:
Expand All @@ -114,6 +113,8 @@ def _init_defaults(
logger=self.logger,
zfs=self.zfs
)
self.__user_provided_defaults = None
self._defaults.read_config()

@property
def id(self) -> str:
Expand All @@ -131,9 +132,12 @@ def id(self) -> str:
@property
def defaults(self) -> 'libioc.Resource.DefaultResource':
"""Return the lazy-loaded defaults."""
if self._defaults_initialized is False:
self._defaults.read_config()
self._defaults_initialized = True
try:
return self._defaults
except AttributeError:
pass

self.__init_defaults()
return self._defaults

@property
Expand Down Expand Up @@ -187,8 +191,11 @@ def processor(self) -> str:
@property
def ipfw_enabled(self) -> bool:
"""Return True if ipfw is enabled on the host system."""
_sysctl = sysctl.filter("net.inet.ip.fw.enable")
return ((len(_sysctl) == 1) and (_sysctl[0].value == 1))
try:
firewall_enabled = freebsd_sysctl.Sysctl("net.inet.ip.fw.enable")
return (firewall_enabled.value == 1) is True
except Exception:
return False


class Host(HostGenerator):
Expand Down
135 changes: 56 additions & 79 deletions libioc/Jail.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import shutil

import libzfs
import freebsd_sysctl

import libioc.Types
import libioc.errors
Expand Down Expand Up @@ -1653,93 +1654,69 @@ def devfs_ruleset(self) -> libioc.DevfsRules.DevfsRuleset:
ruleset_line_position = self.host.devfs.index(devfs_ruleset)
return self.host.devfs[ruleset_line_position].number

@property
def _launch_command(self) -> typing.List[str]:

command = ["/usr/sbin/jail", "-c"]
@staticmethod
def __get_launch_command(jail_args: typing.List[str]) -> typing.List[str]:
return ["/usr/sbin/jail", "-c"] + jail_args

if self.config["vnet"]:
command.append("vnet")
else:
@property
def _launch_args(self) -> typing.List[str]:
config = self.config
vnet = (config["vnet"] is True)
value: str
jail_param_args: typing.List[str] = []
for sysctl_name, sysctl in libioc.JailParams.JailParams().items():
if sysctl.ctl_type == freebsd_sysctl.types.NODE:
# skip NODE
continue

if self.config["ip4_addr"] is not None:
ip4_addr = self.config["ip4_addr"]
command += [
f"ip4.addr={ip4_addr}",
f"ip4.saddrsel={self.config['ip4_saddrsel']}",
f"ip4={self.config['ip4']}",
]
if sysctl_name == "security.jail.param.devfs_ruleset":
value = str(self.devfs_ruleset)
elif sysctl_name == "security.jail.param.path":
value = self.root_dataset.mountpoint
elif sysctl_name == "security.jail.param.name":
value = self.identifier
elif sysctl_name == "security.jail.param.allow.mount.zfs":
value = str(self._allow_mount_zfs)
elif sysctl_name == "security.jail.param.vnet":
if vnet is False:
# vnet is only used when explicitly enabled
# (friendly to Kernels without VIMAGE support)
continue
value = "vnet"
elif vnet and sysctl_name.startswith("security.jail.param.ip"):
continue
else:
config_property_name = sysctl.iocage_name
if self.config._is_known_property(config_property_name):
value = config[config_property_name]
else:
continue

if self.config['ip6_addr'] is not None:
ip6_addr = self.config['ip6_addr']
command += [
f"ip6.addr={ip6_addr}",
f"ip6.saddrsel={self.config['ip6_saddrsel']}",
f"ip6={self.config['ip6']}",
]
sysctl.value = value
jail_param_args.append(str(sysctl))

command += [
f"name={self.identifier}",
f"host.hostname={self.config['host_hostname']}",
f"host.domainname={self.config['host_domainname']}",
f"path={self.root_dataset.mountpoint}",
f"securelevel={self._get_value('securelevel')}",
f"host.hostuuid={self.name}",
f"devfs_ruleset={self.devfs_ruleset}",
f"enforce_statfs={self._get_value('enforce_statfs')}",
f"children.max={self._get_value('children_max')}",
f"allow.set_hostname={self._get_value('allow_set_hostname')}",
f"allow.sysvipc={self._get_value('allow_sysvipc')}",
jail_args = [
f"exec.timeout={self._get_value('exec_timeout')}",
f"stop.timeout={self._get_value('stop_timeout')}",
f"exec.prestart=\"{self.get_hook_script_path('prestart')}\"",
f"exec.prestop=\"{self.get_hook_script_path('prestop')}\"",
f"exec.poststop=\"{self.get_hook_script_path('poststop')}\"",
f"exec.jail_user={self._get_value('exec_jail_user')}"
]

if self.host.userland_version > 10.3:
command += [
f"sysvmsg={self._get_value('sysvmsg')}",
f"sysvsem={self._get_value('sysvsem')}",
f"sysvshm={self._get_value('sysvshm')}"
]

command += [
f"allow.raw_sockets={self._get_value('allow_raw_sockets')}",
f"allow.chflags={self._get_value('allow_chflags')}",
f"allow.mount={self._allow_mount}",
f"allow.mount.devfs={self._get_value('allow_mount_devfs')}",
f"allow.mount.nullfs={self._get_value('allow_mount_nullfs')}",
f"allow.mount.procfs={self._get_value('allow_mount_procfs')}",
f"allow.mount.fdescfs={self._get_value('allow_mount_fdescfs')}",
f"allow.mount.zfs={self._allow_mount_zfs}",
f"allow.quotas={self._get_value('allow_quotas')}",
f"allow.socket_af={self._get_value('allow_socket_af')}",
f"exec.timeout={self._get_value('exec_timeout')}",
f"stop.timeout={self._get_value('stop_timeout')}",
f"exec.jail_user={self._get_value('exec_jail_user')}",
f"mount.fstab={self.fstab.path}",
f"mount.devfs={self._get_value('mount_devfs')}"
f"mount.devfs={self._get_value('mount_devfs')}",
"allow.dying"
]

if self.config["allow_vmm"] is True:
command.append("allow.vmm=1")

if self.host.userland_version > 9.3:
command += [
f"mount.fdescfs={self._get_value('mount_fdescfs')}",
f"allow.mount.tmpfs={self._get_value('allow_mount_tmpfs')}"
]

command += ["allow.dying"]
return command
return jail_param_args + jail_args

def _launch_persistent_jail(
self,
passthru: bool
) -> libioc.helpers.CommandOutput:
command = self._launch_command + [
command = self.__get_launch_command(self._launch_args + [
"persist",
f"exec.poststart=\"{self.get_hook_script_path('poststart')}\""
]
])

stdout, stderr, returncode = self._exec_host_command(
command=command,
Expand Down Expand Up @@ -1799,11 +1776,11 @@ def _launch_single_command_jail(
jail_command: str,
passthru: bool
) -> libioc.helpers.CommandOutput:
command = self._launch_command + [
command = self.__get_launch_command(self._launch_args + [
"nopersist",
f"exec.poststart=\"{self.get_hook_script_path('host_command')}\"",
"command=/usr/bin/true"
]
])

_identifier = str(shlex.quote(self.identifier))
_jls_command = f"/usr/sbin/jls -j {_identifier} jid"
Expand Down Expand Up @@ -2009,16 +1986,16 @@ def _clear_resource_limits(self) -> typing.List[str]:
return [f"/usr/bin/rctl -r jail:{self.identifier} 2>/dev/null || true"]

@property
def _allow_mount(self) -> str:
if self._allow_mount_zfs == "1":
return "1"
return self._get_value("allow_mount")
def _allow_mount(self) -> int:
if self._allow_mount_zfs == 1:
return 1
return int(self._get_value("allow_mount"))

@property
def _allow_mount_zfs(self) -> str:
def _allow_mount_zfs(self) -> int:
if self.config["jail_zfs"] is True:
return "1"
return self._get_value("allow_mount_zfs")
return 1
return int(self._get_value("allow_mount_zfs"))

def _configure_routes_commands(self) -> typing.List[str]:

Expand Down
Loading