mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-11-02 19:02:31 +00:00
feat(oci): update and modify every application, and keep its address
- The backup taken before an update or a modification is made on another storage when it does not fit on the system disk; an operation without room says so before anything is stopped. - Privileged containers, relaxed AppArmor or seccomp, process limits, the host monitors Glances and Netdata and single-file host mounts are rebuilt and recovered like any other application. - HAOS One installs the docker-haos image: port 8123, host time zone, unprivileged, watchdog proposed by default; it is updated and modified with Home Assistant's own check on the rebuilt container. - A container that reboots itself gets its DHCP client back. - The installer takes the address of the container's own interface, and the notes of a suite or stack container show its LAN address. - Seerr installs: its volume belongs to the image user and no-new-privileges goes through the include of the container. - Removing an application deletes its seccomp profile.
This commit is contained in:
@@ -18,6 +18,9 @@ def sysctl_content(deployment):
|
||||
raise ValueError(translate('Invalid or duplicated network sysctl'))
|
||||
seen.add(name)
|
||||
result.append(f'lxc.sysctl.{name} = {value}\n')
|
||||
# Proxmox does not take this setting in the configuration of a container.
|
||||
if (deployment.get('security', {}).get('options') or {}).get('no_new_privileges'):
|
||||
result.append('lxc.no_new_privs = 1\n')
|
||||
return ''.join(result)
|
||||
|
||||
|
||||
@@ -58,6 +61,28 @@ def legacy_include_path(vmid):
|
||||
return Path(f'/etc/pve/lxc/{int(vmid)}.proxmenux-sysctls')
|
||||
|
||||
|
||||
# A host monitor shares the process and network namespaces of the host. Proxmox
|
||||
# takes those two settings from an include, the same for every monitor.
|
||||
HOST_MONITOR_INCLUDES = (CLUSTER_DIR / 'host-monitor', Path('/etc/pve/lxc/proxmenux-host-monitor'))
|
||||
HOST_MONITOR_CONTENT = 'lxc.namespace.share.pid = 1\nlxc.namespace.share.net = 1\n'
|
||||
|
||||
|
||||
def host_monitor_include(includes):
|
||||
"""Whether the includes are the one of a host monitor, unchanged."""
|
||||
if len(includes) != 1 or Path(includes[0]) not in HOST_MONITOR_INCLUDES:
|
||||
return False
|
||||
try:
|
||||
return Path(includes[0]).read_text() == HOST_MONITOR_CONTENT
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
def seccomp_path(vmid):
|
||||
"""The seccomp profile the installer writes for a container that asks for
|
||||
it relaxed."""
|
||||
return Path(f'/etc/pve/lxc/{int(vmid)}.proxmenux-seccomp')
|
||||
|
||||
|
||||
def check(config, deployment, vmid):
|
||||
expected = tmpfs_lines(deployment)
|
||||
lines = config.decode().splitlines()
|
||||
@@ -65,6 +90,10 @@ def check(config, deployment, vmid):
|
||||
if sorted(actual) != sorted(expected):
|
||||
raise ValueError(translate('The tmpfs mounts of the container differ from the saved record'))
|
||||
includes = [line.split(': ',1)[1] for line in lines if line.startswith('lxc.include: ')]
|
||||
if deployment.get('host_monitor'):
|
||||
if not host_monitor_include(includes):
|
||||
raise ValueError(translate('The sysctl include is unknown or differs from the saved record'))
|
||||
return
|
||||
content = sysctl_content(deployment)
|
||||
allowed = [[str(include_path(vmid))], [str(legacy_include_path(vmid))]] if content else [[]]
|
||||
if includes not in allowed:
|
||||
@@ -78,12 +107,34 @@ def check(config, deployment, vmid):
|
||||
raise ValueError(translate('The sysctl content was modified outside the saved record'))
|
||||
|
||||
|
||||
FILE_BIND = re.compile(r'(/\S+) \S+ none bind,create=file(,ro)? 0 0')
|
||||
|
||||
|
||||
def file_binds(config):
|
||||
"""The host files mounted in the container: {source: read only}. Proxmox
|
||||
takes a directory as a mount point and a single file as a raw LXC entry,
|
||||
so these are not among the mount points."""
|
||||
found = {}
|
||||
for line in config.decode().splitlines():
|
||||
match = FILE_BIND.fullmatch(line.split(': ', 1)[1]) if line.startswith('lxc.mount.entry: ') else None
|
||||
if match:
|
||||
found[match.group(1)] = bool(match.group(2))
|
||||
return found
|
||||
|
||||
|
||||
def filter_config(config, deployment):
|
||||
"""The configuration without the entries of the mounts the deployment
|
||||
declares itself: what is left belongs to the acceleration profile."""
|
||||
declared = set(tmpfs_lines(deployment))
|
||||
files = {m['source'] for m in deployment.get('mounts', []) if m.get('type') == 'host-bind'}
|
||||
|
||||
def own(line):
|
||||
entry = line.decode().strip().split(': ', 1)[1]
|
||||
match = FILE_BIND.fullmatch(entry)
|
||||
return entry in declared or bool(match and match.group(1) in files)
|
||||
return b''.join(line for line in config.splitlines(keepends=True)
|
||||
if not line.startswith(b'lxc.include: ') and not (
|
||||
line.startswith(b'lxc.mount.entry: ')
|
||||
and line.decode().strip().split(': ',1)[1] in declared))
|
||||
line.startswith(b'lxc.mount.entry: ') and own(line)))
|
||||
|
||||
|
||||
def check_recovery(config, state):
|
||||
@@ -94,6 +145,8 @@ def check_recovery(config, state):
|
||||
raise ValueError(translate('A tmpfs mount is not part of the journal; recovery blocked'))
|
||||
if line.startswith('lxc.include: '):
|
||||
path = Path(line.split(': ', 1)[1])
|
||||
if any(plan.get('host_monitor') for plan in plans) and host_monitor_include([str(path)]):
|
||||
continue
|
||||
if path not in (include_path(state['vmid']), legacy_include_path(state['vmid'])):
|
||||
raise ValueError(translate('An include is not part of the journal; recovery blocked'))
|
||||
contents = {sysctl_content(plan) for plan in plans} - {''}
|
||||
|
||||
Reference in New Issue
Block a user