From cbf22f6652ae7fd265b10b9ad12dba6ffcc16d8c Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy Date: Tue, 8 Oct 2024 17:11:53 +0300 Subject: [PATCH 1/5] feat: podman: handle custom network configuration Allow to add custom network configuration to the network bridge activated via podman provider. Signed-off-by: Alexander Bokovoy --- src/mrack/data/provisioning-config.yaml | 8 +++++--- src/mrack/providers/podman.py | 27 +++++++++++++++++++++++-- src/mrack/providers/utils/podman.py | 4 +++- src/mrack/transformers/podman.py | 3 ++- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/mrack/data/provisioning-config.yaml b/src/mrack/data/provisioning-config.yaml index 393a5eca..287b2d6a 100644 --- a/src/mrack/data/provisioning-config.yaml +++ b/src/mrack/data/provisioning-config.yaml @@ -12,6 +12,10 @@ podman: # this will be used as prefix for the network name default_network: mrack + # Options to customize network creation + network_options: + - "--ipv6" + # advanced podman options to be passed to every execution of podman run eg: # NOTE: when 'key' in podman_options has list assigned as value # the option specidfied by the 'key' is added multiple times @@ -21,7 +25,7 @@ podman: "--cap-add": - "ALL" # Allowed syscall list seccomp JSON file to be used as a seccomp filter - "--security-opt": "seccomp=src/mrack/data/seccomp.json" + "--security-opt": "seccomp=/etc/mrack/seccomp.json" # Mount a temporary filesystems (tmpfs) into a container "--tmpfs": - "/tmp" @@ -30,8 +34,6 @@ podman: # Use /sys/fs/cgroup in container as read only volume "-v": - "/sys/fs/cgroup:/sys/fs/cgroup:ro" - # Adding ipv6 support to network - "--network": "enable_ipv6=true" virt: images: diff --git a/src/mrack/providers/podman.py b/src/mrack/providers/podman.py index c12a5204..aa310c6c 100644 --- a/src/mrack/providers/podman.py +++ b/src/mrack/providers/podman.py @@ -50,6 +50,7 @@ async def init( self, container_images, default_network, + network_options, ssh_key, container_options, extra_commands, @@ -63,6 +64,7 @@ async def init( self.max_retry = max_retry self.images = container_images self.default_network = default_network + self.network_options = network_options self.ssh_key = ssh_key self.podman_options = container_options self.extra_commands = extra_commands @@ -93,7 +95,11 @@ async def prepare_provisioning(self, reqs): awaitables = [] for network in network_list: if not await self.podman.network_exists(network): - awaitables.append(self.podman.network_create(network)) + awaitables.append( + self.podman.network_create( + network, options=self.network_options + ) + ) network_results = await asyncio.gather(*awaitables) success = all(network_results) @@ -173,7 +179,8 @@ async def wait_till_provisioned(self, resource): while datetime.now() < timeout_time: try: - server = await self.podman.inspect(cont_id)[0] + servers = await self.podman.inspect(cont_id) + server = servers[0] except ProvisioningError as err: logger.error(f"{log_msg_start} {object2json(err)}") raise ServerNotFoundError(cont_id) from err @@ -223,6 +230,22 @@ async def wait_till_provisioned(self, resource): return server, req + async def _wait_for_ssh(self, host, timeout, port): + log_msg_start = f"{self.dsp_name} [{host}]" + start_ssh = datetime.now() + while True: + res = await self.podman.exec_command( + host._host_id, "systemctl -q is-active sshd" + ) + logger.info(f"{log_msg_start} ran is-active for ssh, result '{res}'") + if not res: + await asyncio.sleep(10) + if datetime.now() - start_ssh >= timedelta(seconds=(timeout * 60)): + break + else: + break + return res, host + async def delete_host(self, host_id, host_name): """Delete provisioned host.""" # if there is no container we do nothing diff --git a/src/mrack/providers/utils/podman.py b/src/mrack/providers/utils/podman.py index 130ea2b5..291ad754 100644 --- a/src/mrack/providers/utils/podman.py +++ b/src/mrack/providers/utils/podman.py @@ -121,7 +121,7 @@ async def network_exists(self, network): _stdout, _stderr, inspect = await self._run_podman(args, raise_on_err=False) return inspect.returncode == 0 - async def network_create(self, network): + async def network_create(self, network, options=None): """Create a podman network if it does not exist.""" if await self.network_exists(network): logger.debug(f"{self.dsp_name} Network '{network}' is present") @@ -129,6 +129,8 @@ async def network_create(self, network): logger.info(f"{self.dsp_name} Creating podman network '{network}'") args = ["network", "create", network] + if options: + args.extend(options) _stdout, _stderr, process = await self._run_podman(args, raise_on_err=False) return process.returncode == 0 diff --git a/src/mrack/transformers/podman.py b/src/mrack/transformers/podman.py index a78a0d50..dfae59a9 100644 --- a/src/mrack/transformers/podman.py +++ b/src/mrack/transformers/podman.py @@ -38,8 +38,9 @@ async def init_provider(self): await self._provider.init( container_images=self.config["images"].values(), - ssh_key=self.config["pubkey"], default_network=self.config["default_network"], + network_options=self.config.get("network_options", []), + ssh_key=self.config["pubkey"], container_options=self.config["podman_options"], extra_commands=self.config.get("extra_commands", []), strategy=self.config.get("strategy", STRATEGY_ABORT), From 1265245382b38867b5299d7d78b183d8a6296cfd Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy Date: Tue, 8 Oct 2024 17:13:06 +0300 Subject: [PATCH 2/5] docs: Update seccomp configuration example seccomp.json example from FreeIPA Azure CI tests. It works well for both docker and podman, both root and rootless. Signed-off-by: Alexander Bokovoy --- src/mrack/data/seccomp.json | 414 ++++++++++++++++++++++++++++-------- 1 file changed, 328 insertions(+), 86 deletions(-) diff --git a/src/mrack/data/seccomp.json b/src/mrack/data/seccomp.json index 16becf98..7d50588e 100644 --- a/src/mrack/data/seccomp.json +++ b/src/mrack/data/seccomp.json @@ -1,5 +1,7 @@ { - "defaultAction": "SCMP_ACT_LOG", + "__defaultAction": "Change defaultAction to SCMP_ACT_LOG and then check Host's journal for SECCOMP", + "defaultAction": "SCMP_ACT_ERRNO", + "defaultErrnoRet": 38, "archMap": [ { "architecture": "SCMP_ARCH_X86_64", @@ -52,6 +54,46 @@ "syscalls": [ { "names": [ + "bdflush", + "io_pgetevents", + "kexec_file_load", + "kexec_load", + "migrate_pages", + "move_pages", + "nfsservctl", + "nice", + "oldfstat", + "oldlstat", + "oldolduname", + "oldstat", + "olduname", + "pciconfig_iobase", + "pciconfig_read", + "pciconfig_write", + "sgetmask", + "ssetmask", + "swapcontext", + "swapoff", + "swapon", + "sysfs", + "uselib", + "userfaultfd", + "ustat", + "vm86", + "vm86old", + "vmsplice" + ], + "action": "SCMP_ACT_ERRNO", + "args": [], + "comment": "", + "includes": {}, + "excludes": {}, + "errnoRet": 1 + }, + { + "names": [ + "_llseek", + "_newselect", "accept", "accept4", "access", @@ -66,10 +108,17 @@ "chown", "chown32", "clock_adjtime", + "clock_adjtime64", "clock_getres", + "clock_getres_time64", "clock_gettime", + "clock_gettime64", "clock_nanosleep", + "clock_nanosleep_time64", + "clone", + "clone3", "close", + "close_range", "connect", "copy_file_range", "creat", @@ -81,6 +130,7 @@ "epoll_ctl", "epoll_ctl_old", "epoll_pwait", + "epoll_pwait2", "epoll_wait", "epoll_wait_old", "eventfd", @@ -109,7 +159,11 @@ "flock", "fork", "fremovexattr", + "fsconfig", "fsetxattr", + "fsmount", + "fsopen", + "fspick", "fstat", "fstat64", "fstatat64", @@ -119,7 +173,10 @@ "ftruncate", "ftruncate64", "futex", + "futex_time64", "futimesat", + "get_robust_list", + "get_thread_area", "getcpu", "getcwd", "getdents", @@ -133,6 +190,7 @@ "getgroups", "getgroups32", "getitimer", + "get_mempolicy", "getpeername", "getpgid", "getpgrp", @@ -145,12 +203,10 @@ "getresuid", "getresuid32", "getrlimit", - "get_robust_list", "getrusage", "getsid", "getsockname", "getsockopt", - "get_thread_area", "gettid", "gettimeofday", "getuid", @@ -161,14 +217,15 @@ "inotify_init1", "inotify_rm_watch", "io_cancel", - "ioctl", "io_destroy", "io_getevents", - "ioprio_get", - "ioprio_set", "io_setup", "io_submit", + "ioctl", + "ioprio_get", + "ioprio_set", "ipc", + "keyctl", "kill", "lchown", "lchown32", @@ -178,14 +235,15 @@ "listen", "listxattr", "llistxattr", - "_llseek", "lremovexattr", "lseek", "lsetxattr", "lstat", "lstat64", "madvise", + "mbind", "memfd_create", + "memfd_secret", "mincore", "mkdir", "mkdirat", @@ -196,12 +254,16 @@ "mlockall", "mmap", "mmap2", + "mount", + "move_mount", "mprotect", "mq_getsetattr", "mq_notify", "mq_open", "mq_timedreceive", + "mq_timedreceive_time64", "mq_timedsend", + "mq_timedsend_time64", "mq_unlink", "mremap", "msgctl", @@ -212,33 +274,47 @@ "munlock", "munlockall", "munmap", + "name_to_handle_at", "nanosleep", "newfstatat", - "_newselect", "open", "openat", + "openat2", + "open_tree", "pause", + "pidfd_getfd", + "pidfd_open", + "pidfd_send_signal", "pipe", "pipe2", + "pivot_root", + "pkey_alloc", + "pkey_free", + "pkey_mprotect", "poll", "ppoll", + "ppoll_time64", "prctl", "pread64", "preadv", "preadv2", "prlimit64", "pselect6", + "pselect6_time64", "pwrite64", "pwritev", "pwritev2", "read", "readahead", + "readdir", "readlink", "readlinkat", "readv", + "reboot", "recv", "recvfrom", "recvmmsg", + "recvmmsg_time64", "recvmsg", "remap_file_pages", "removexattr", @@ -247,6 +323,7 @@ "renameat2", "restart_syscall", "rmdir", + "rseq", "rt_sigaction", "rt_sigpending", "rt_sigprocmask", @@ -254,14 +331,16 @@ "rt_sigreturn", "rt_sigsuspend", "rt_sigtimedwait", + "rt_sigtimedwait_time64", "rt_tgsigqueueinfo", + "sched_get_priority_max", + "sched_get_priority_min", "sched_getaffinity", "sched_getattr", "sched_getparam", - "sched_get_priority_max", - "sched_get_priority_min", "sched_getscheduler", "sched_rr_get_interval", + "sched_rr_get_interval_time64", "sched_setaffinity", "sched_setattr", "sched_setparam", @@ -273,12 +352,18 @@ "semget", "semop", "semtimedop", + "semtimedop_time64", "send", "sendfile", "sendfile64", "sendmmsg", "sendmsg", "sendto", + "setns", + "set_mempolicy", + "set_robust_list", + "set_thread_area", + "set_tid_address", "setfsgid", "setfsgid32", "setfsuid", @@ -299,11 +384,8 @@ "setreuid", "setreuid32", "setrlimit", - "set_robust_list", "setsid", "setsockopt", - "set_thread_area", - "set_tid_address", "setuid", "setuid32", "setxattr", @@ -316,7 +398,6 @@ "signalfd", "signalfd4", "sigreturn", - "socket", "socketcall", "socketpair", "splice", @@ -331,41 +412,44 @@ "sync_file_range", "syncfs", "sysinfo", + "syslog", "tee", "tgkill", "time", "timer_create", "timer_delete", - "timerfd_create", - "timerfd_gettime", - "timerfd_settime", "timer_getoverrun", "timer_gettime", + "timer_gettime64", "timer_settime", + "timer_settime64", + "timerfd_create", + "timerfd_gettime", + "timerfd_gettime64", + "timerfd_settime", + "timerfd_settime64", "times", "tkill", "truncate", "truncate64", "ugetrlimit", "umask", + "umount", + "umount2", "uname", "unlink", "unlinkat", + "unshare", "utime", "utimensat", + "utimensat_time64", "utimes", "vfork", - "vmsplice", "wait4", "waitid", "waitpid", "write", - "writev", - "mount", - "umount2", - "reboot", - "name_to_handle_at", - "unshare" + "writev" ], "action": "SCMP_ACT_ALLOW", "args": [], @@ -554,23 +638,31 @@ }, "excludes": {} }, + { + "names": [ + "open_by_handle_at" + ], + "action": "SCMP_ACT_ERRNO", + "args": [], + "comment": "", + "includes": {}, + "excludes": { + "caps": [ + "CAP_DAC_READ_SEARCH" + ] + }, + "errnoRet": 1 + }, { "names": [ "bpf", - "clone", "fanotify_init", "lookup_dcookie", - "mount", - "name_to_handle_at", "perf_event_open", "quotactl", "setdomainname", "sethostname", - "setns", - "syslog", - "umount", - "umount2", - "unshare" + "setns" ], "action": "SCMP_ACT_ALLOW", "args": [], @@ -584,79 +676,68 @@ }, { "names": [ - "clone" - ], - "action": "SCMP_ACT_ALLOW", - "args": [ - { - "index": 0, - "value": 2080505856, - "valueTwo": 0, - "op": "SCMP_CMP_MASKED_EQ" - } + "bpf", + "fanotify_init", + "lookup_dcookie", + "perf_event_open", + "quotactl", + "setdomainname", + "sethostname", + "setns" ], + "action": "SCMP_ACT_ERRNO", + "args": [], "comment": "", "includes": {}, "excludes": { "caps": [ "CAP_SYS_ADMIN" - ], - "arches": [ - "s390", - "s390x" ] - } + }, + "errnoRet": 1 }, { "names": [ - "clone" + "chroot" ], "action": "SCMP_ACT_ALLOW", - "args": [ - { - "index": 1, - "value": 2080505856, - "valueTwo": 0, - "op": "SCMP_CMP_MASKED_EQ" - } - ], - "comment": "s390 parameter ordering for clone is different", + "args": [], + "comment": "", "includes": { - "arches": [ - "s390", - "s390x" - ] - }, - "excludes": { "caps": [ - "CAP_SYS_ADMIN" + "CAP_SYS_CHROOT" ] - } + }, + "excludes": {} }, { "names": [ - "reboot" + "chroot" ], - "action": "SCMP_ACT_ALLOW", + "action": "SCMP_ACT_ERRNO", "args": [], "comment": "", - "includes": { + "includes": {}, + "excludes": { "caps": [ - "CAP_SYS_BOOT" + "CAP_SYS_CHROOT" ] }, - "excludes": {} + "errnoRet": 1 }, { "names": [ - "chroot" + "delete_module", + "init_module", + "finit_module", + "query_module" ], "action": "SCMP_ACT_ALLOW", "args": [], "comment": "", "includes": { "caps": [ - "CAP_SYS_CHROOT" + "CAP_SYS_MODULE" ] }, "excludes": {} @@ -668,15 +749,16 @@ "finit_module", "query_module" ], - "action": "SCMP_ACT_ALLOW", + "action": "SCMP_ACT_ERRNO", "args": [], "comment": "", - "includes": { + "includes": {}, + "excludes": { "caps": [ "CAP_SYS_MODULE" ] }, - "excludes": {} + "errnoRet": 1 }, { "names": [ @@ -692,9 +774,25 @@ }, "excludes": {} }, + { + "names": [ + "acct" + ], + "action": "SCMP_ACT_ERRNO", + "args": [], + "comment": "", + "includes": {}, + "excludes": { + "caps": [ + "CAP_SYS_PACCT" + ] + }, + "errnoRet": 1 + }, { "names": [ "kcmp", + "process_madvise", "process_vm_readv", "process_vm_writev", "ptrace" @@ -709,6 +807,25 @@ }, "excludes": {} }, + { + "names": [ + "kcmp", + "process_madvise", + "process_vm_readv", + "process_vm_writev", + "ptrace" + ], + "action": "SCMP_ACT_ERRNO", + "args": [], + "comment": "", + "includes": {}, + "excludes": { + "caps": [ + "CAP_SYS_PTRACE" + ] + }, + "errnoRet": 1 + }, { "names": [ "iopl", @@ -724,11 +841,28 @@ }, "excludes": {} }, + { + "names": [ + "iopl", + "ioperm" + ], + "action": "SCMP_ACT_ERRNO", + "args": [], + "comment": "", + "includes": {}, + "excludes": { + "caps": [ + "CAP_SYS_RAWIO" + ] + }, + "errnoRet": 1 + }, { "names": [ "settimeofday", "stime", - "clock_settime" + "clock_settime", + "clock_settime64" ], "action": "SCMP_ACT_ALLOW", "args": [], @@ -740,6 +874,24 @@ }, "excludes": {} }, + { + "names": [ + "settimeofday", + "stime", + "clock_settime", + "clock_settime64" + ], + "action": "SCMP_ACT_ERRNO", + "args": [], + "comment": "", + "includes": {}, + "excludes": { + "caps": [ + "CAP_SYS_TIME" + ] + }, + "errnoRet": 1 + }, { "names": [ "vhangup" @@ -756,30 +908,120 @@ }, { "names": [ - "get_mempolicy", - "mbind", - "set_mempolicy" + "vhangup" ], - "action": "SCMP_ACT_ALLOW", + "action": "SCMP_ACT_ERRNO", "args": [], "comment": "", - "includes": { + "includes": {}, + "excludes": { "caps": [ - "CAP_SYS_NICE" + "CAP_SYS_TTY_CONFIG" ] }, - "excludes": {} + "errnoRet": 1 + }, + { + "names": [ + "socket" + ], + "action": "SCMP_ACT_ERRNO", + "args": [ + { + "index": 0, + "value": 16, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + }, + { + "index": 2, + "value": 9, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ], + "comment": "", + "includes": {}, + "excludes": { + "caps": [ + "CAP_AUDIT_WRITE" + ] + }, + "errnoRet": 22 }, { "names": [ - "syslog" + "socket" ], "action": "SCMP_ACT_ALLOW", - "args": [], + "args": [ + { + "index": 2, + "value": 9, + "valueTwo": 0, + "op": "SCMP_CMP_NE" + } + ], + "comment": "", + "includes": {}, + "excludes": { + "caps": [ + "CAP_AUDIT_WRITE" + ] + } + }, + { + "names": [ + "socket" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 16, + "valueTwo": 0, + "op": "SCMP_CMP_NE" + } + ], + "comment": "", + "includes": {}, + "excludes": { + "caps": [ + "CAP_AUDIT_WRITE" + ] + } + }, + { + "names": [ + "socket" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 2, + "value": 9, + "valueTwo": 0, + "op": "SCMP_CMP_NE" + } + ], + "comment": "", + "includes": {}, + "excludes": { + "caps": [ + "CAP_AUDIT_WRITE" + ] + } + }, + { + "names": [ + "socket" + ], + "action": "SCMP_ACT_ALLOW", + "args": null, "comment": "", "includes": { "caps": [ - "CAP_SYSLOG" + "CAP_AUDIT_WRITE" ] }, "excludes": {} From 2ebd729d4eddc52624096f7f980c7325a902952e Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy Date: Tue, 8 Oct 2024 17:19:37 +0300 Subject: [PATCH 3/5] chore: setup: package seccomp filter Add seccomp.json to setup mrack package so it is included on pkg install. Signed-off-by: Alexander Bokovoy --- setup.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 95804e00..3e88fdbf 100644 --- a/setup.py +++ b/setup.py @@ -35,6 +35,7 @@ MRACK_CONF = "mrack.conf" PROV_CONF = "provisioning-config.yaml" +SECCOMP_JSON = "seccomp.json" setup( name="mrack", @@ -52,7 +53,9 @@ install_requires=reqs, scripts=["scripts/mrack"], package_data={ - "mrack": [f"data/{datafile}" for datafile in [MRACK_CONF, PROV_CONF]] + "mrack": [ + f"data/{datafile}" for datafile in [MRACK_CONF, PROV_CONF, SECCOMP_JSON] + ] }, extras_require={ "krb-owner": ["gssapi"], From 4b831aa41952871c686304f4f382cd2f96fb5151 Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy Date: Tue, 8 Oct 2024 17:37:00 +0300 Subject: [PATCH 4/5] fix: podman: set podman connection information for ansible Ansible connection.podman.podman connection module uses ansible_host as a container ID to connect to. Use container ID instead of IP address which cannot be reached in rootless setup anyway. It makes `ansible -c podman -i metadata-inventory.yaml` usable in rootless podman setup because one cannot connect over IP addresses to the containers as the networking bridge is not visible from the host. Signed-off-by: Alexander Bokovoy --- src/mrack/providers/podman.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mrack/providers/podman.py b/src/mrack/providers/podman.py index aa310c6c..773997b4 100644 --- a/src/mrack/providers/podman.py +++ b/src/mrack/providers/podman.py @@ -311,6 +311,9 @@ def prov_result_to_host_data(self, prov_result, req): result["status"] = status result["os"] = prov_result.get("mrack_req").get("os") result["group"] = prov_result.get("mrack_req").get("group") + meta_extra = {} + meta_extra["ansible_host"] = result["id"] + result["meta_extra"] = meta_extra return result From 0683fc891c98423d798180ebafc1aeb66ef93ecc Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy Date: Thu, 10 Oct 2024 13:56:21 +0300 Subject: [PATCH 5/5] style: Reformat by black --- docs/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 5f0eadf8..3d295b67 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -32,7 +32,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ['m2r2'] +extensions = ["m2r2"] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] @@ -54,4 +54,4 @@ # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] -source_suffix = ['.rst', '.md'] +source_suffix = [".rst", ".md"]