From 2e686ef9016a88825f6ffaadbe0b268bee2a5fb1 Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Mon, 19 May 2025 12:40:52 -0600 Subject: [PATCH 01/28] Redoing layer installs so that if you are importing from an existing image (not scratch) then use the package manager installed in the image Signed-off-by: Travis Cotton --- src/image-build | 2 +- src/installer.py | 102 ++++++++++++++++++++++++++++++++++++++--------- src/layer.py | 61 ++++++++++++++++++---------- 3 files changed, 124 insertions(+), 41 deletions(-) diff --git a/src/image-build b/src/image-build index 2351aea..023f5cf 100755 --- a/src/image-build +++ b/src/image-build @@ -56,7 +56,7 @@ def main(): level = getattr(logging, args['log_level'].upper(), 10) logging.basicConfig(format='%(levelname)s - %(message)s',level=level) - print_args(args) + print_args(args) except argparse.ArgumentError as e: print(f"Argument error: {e}") diff --git a/src/installer.py b/src/installer.py index 524389e..5ac55a7 100644 --- a/src/installer.py +++ b/src/installer.py @@ -21,7 +21,7 @@ def __init__(self, pkg_man, cname, mname): # DNF complains if the log directory is not present os.makedirs(os.path.join(self.tdir, "dnf/log")) - def install_repos(self, repos, repo_dest, proxy): + def install_scratch_repos(self, repos, repo_dest, proxy): # check if there are repos passed for install if len(repos) == 0: logging.info("REPOS: no repos passed to install\n") @@ -92,7 +92,7 @@ def install_repos(self, repos, repo_dest, proxy): if rc != 0: raise Exception("Failed to install gpg key for", r['alias'], "at URL", r['gpg']) - def install_base_packages(self, packages, registry_loc, proxy): + def install_scratch_packages(self, packages, registry_loc, proxy): # check if there are packages to install if len(packages) == 0: logging.warn("PACKAGES: no packages passed to install\n") @@ -134,19 +134,7 @@ def install_base_packages(self, packages, registry_loc, proxy): if rc == 107: logging.warn("one or more RPM postscripts failed to run") - def remove_base_packages(self, remove_packages): - # check if there are packages to remove - if len(remove_packages) == 0: - logging.warn("REMOVE PACKAGES: no package passed to remove\n") - return - - logging.info(f"REMOVE PACKAGES: removing these packages from container {self.cname}") - logging.info("\n".join(remove_packages)) - for p in remove_packages: - args = [self.cname, '--', 'rpm', '-e', '--nodeps', p] - cmd(["buildah","run"] + args) - - def install_base_package_groups(self, package_groups, registry_loc, proxy): + def install_scratch_package_groups(self, package_groups, registry_loc, proxy): # check if there are packages groups to install if len(package_groups) == 0: logging.warn("PACKAGE GROUPS: no package groups passed to install\n") @@ -175,7 +163,7 @@ def install_base_package_groups(self, package_groups, registry_loc, proxy): if rc == 104: raise Exception("Installing base packages failed") - def install_base_modules(self, modules, registry_loc, proxy): + def install_scratch_modules(self, modules, registry_loc, proxy): # check if there are modules groups to install if len(modules) == 0: logging.warn("PACKAGE MODULES: no modules passed to install\n") @@ -205,8 +193,86 @@ def install_base_modules(self, modules, registry_loc, proxy): if rc != 0: raise Exception("Failed to run module cmd", mod_cmd, ' '.join(mod_list)) + def install_repos(self, repos, proxy): + # check if there are repos passed for install + if len(repos) == 0: + logging.info("REPOS: no repos passed to install\n") + return + + logging.info(f"REPOS: Installing these repos to {self.cname}") + for r in repos: + logging.info(r['alias'] + ': ' + r['url']) + if self.pkg_man == "zypper": + if 'priority' in r: + priority = r['priority'] + else: + priority = 99 + rargs = ['addrepo','-f','-p', priority, r['url'], r['alias']] + elif self.pkg_man == "dnf": + rargs = ['config-manager','--save','--add-repo', r['url']] + + args = [self.cname, '--', 'bash', '-c', self.pkg_man] + rargs + rc = cmd(["buildah","run"] + [self.pkg_man] + args) + if rc != 0: + raise Exception("Failed to install repo", r['alias'], r['url']) + # Set Proxy if using DNF + if proxy != "": + if r['url'].endswith('.repo'): + repo_name = r['url'].split('/')[-1].split('.repo')[0] + "*" + elif r['url'].startswith('https'): + repo_name = r['url'].split('https://')[1].replace('/','_') + elif r['url'].startswith('http'): + repo_name = r['url'].split('http://')[1].replace('/','_') + pargs = ['config-manager', '--save', '--setopt=*.proxy=',proxy, repo_name] + + args = [self.cname, '--', 'bash', '-c', self.pkg_man] + pargs + rc = cmd(["buildah","run"] + [self.pkg_man] + args) + if rc != 0: + raise Exception("Failed to set proxy for repo", r['alias'], r['url'], proxy) + + if "gpg" in r: + # Using rpm apparently works for both Yum- and Zypper-based distros. + gargs = ['rpm', '--import', r['gpg']] + if proxy != "": + arg_env = os.environ.copy() + arg_env['https_proxy'] = proxy + rc = cmd(["buildah","run"] + gargs) + if rc != 0: + raise Exception("Failed to install gpg key for", r['alias'], "at URL", r['gpg']) + + def install_packages(self, packages): + if len(packages) == 0: + logging.warn("PACKAGE GROUPS: no package groups passed to install\n") + return + logging.info(f"PACKAGES: Installing these packages to {self.cname}") + logging.info("\n".join(packages)) + args = [self.cname, '--', 'bash', '-c', self.pkg_man, 'install'] + packages + cmd(["buildah","run"] + args) + + def install_package_groups(self, package_groups): + if len(package_groups) == 0: + logging.warn("PACKAGE GROUPS: no package groups passed to install\n") + return + logging.info(f"PACKAGES: Installing these package groups to {self.cname}") + logging.info("\n".join(package_groups)) + if self.pkg_man == "zypper": + logging.warn("zypper does not support package groups") + args = [self.cname, '--', 'bash', '-c', self.pkg_man] + package_groups + cmd(["buildah","run"] + args) + + def remove_packages(self, remove_packages): + # check if there are packages to remove + if len(remove_packages) == 0: + logging.warn("REMOVE PACKAGES: no package passed to remove\n") + return + + logging.info(f"REMOVE PACKAGES: removing these packages from container {self.cname}") + logging.info("\n".join(remove_packages)) + for p in remove_packages: + args = [self.cname, '--', 'rpm', '-e', '--nodeps', p] + cmd(["buildah","run"] + args) - def install_base_commands(self, commands): + def install_commands(self, commands): # check if there are commands to install if len(commands) == 0: logging.warn("COMMANDS: no commands passed to run\n") @@ -230,7 +296,7 @@ def install_base_commands(self, commands): loglevel = logging.error out = cmd(build_cmd + args, stderr_handler=loglevel) - def install_base_copyfiles(self, copyfiles): + def install_copyfiles(self, copyfiles): if len(copyfiles) == 0: logging.warn("COPYFILES: no files to copy\n") return diff --git a/src/layer.py b/src/layer.py index 30b2eb6..68c46e6 100644 --- a/src/layer.py +++ b/src/layer.py @@ -16,37 +16,46 @@ def __init__(self, args, image_config): self.image_config = image_config self.logger = logging.getLogger(__name__) - def buildah_handler(line): - out.append(line) - return out - def _build_base(self, repos, modules, packages, package_groups, remove_packages, commands, copyfiles, oscap_options): + # Set local variables dt_string = datetime.now().strftime("%Y%m%d%H%M%S") + parent = self.args['parent'] + container = self.args['name'] + registry_opts_pull = self.args['registry_opts_pull'] + package_manager = self.args['pkg_man'] + if 'proxy' in self.args: + proxy = self.args['proxy'] + else: + proxy = "" # container and mount name def buildah_handler(line): out.append(line) + # Create a new container from parent out = [] - cmd(["buildah", "from"] + self.args['registry_opts_pull'] + ["--name", self.args['name']+ dt_string, self.args['parent']], stdout_handler = buildah_handler) + cmd(["buildah", "from"] + registry_opts_pull + ["--name", container + dt_string, parent], stdout_handler = buildah_handler) cname = out[0] - out = [] - cmd(["buildah", "mount"] + [cname], stdout_handler = buildah_handler) - mname = out[0] - - self.logger.info(f"Container: {cname} mounted at {mname}") + # Only mount when doing a scratch install + if parent == "scratch": + out = [] + cmd(["buildah", "mount"] + [cname], stdout_handler = buildah_handler) + mname = out[0] + self.logger.info(f"Container: {cname} mounted at {mname}") + else: + mname = "" - if self.args['pkg_man'] == "zypper": + if package_manager == "zypper": repo_dest = "/etc/zypp/repos.d" - elif self.args['pkg_man'] == "dnf": + elif package_manager == "dnf": repo_dest = "/etc/yum.repos.d" else: self.logger.error("unsupported package manager") inst = None try: - inst = installer.Installer(self.args['pkg_man'], cname, mname) + inst = installer.Installer(package_manager, cname, mname) except Exception as e: self.logger.error(f"Error preparing installer: {e}") cmd(["buildah","rm"] + [cname]) @@ -57,8 +66,11 @@ def buildah_handler(line): sys.exit("Exiting now ...") # Install Repos - try: - inst.install_repos(repos, repo_dest, self.args['proxy']) + try: + if parent == "scratch": + inst.install_scratch_repos(repos, repo_dest, proxy) + else: + inst.install_repos(repos, proxy) except Exception as e: self.logger.error(f"Error installing repos: {e}") cmd(["buildah","rm"] + [cname]) @@ -70,14 +82,19 @@ def buildah_handler(line): # Install Packages try: - # Enable modules - inst.install_base_modules(modules, repo_dest, self.args['proxy']) - # Base Package Groups - inst.install_base_package_groups(package_groups, repo_dest, self.args['proxy']) - # Packages - inst.install_base_packages(packages, repo_dest, self.args['proxy']) + if parent == "scratch": + # Enable modules + inst.install_scratch_modules(modules, repo_dest, self.args['proxy']) + # Base Package Groups + inst.install_scratch_package_groups(package_groups, repo_dest, proxy) + # Packages + inst.install_scratch_packages(packages, repo_dest, proxy) + else: + inst.install_modules(modules, repo_dest, self.args['proxy']) + inst.install_package_groups(package_groups, repo_dest, proxy) + inst.install_packages(packages, repo_dest, proxy) # Remove Packages - inst.remove_base_packages(remove_packages) + inst.remove_packages(remove_packages) except Exception as e: self.logger.error(f"Error installing packages: {e}") cmd(["buildah","rm"] + [cname]) From 734d1ed640168271aa826ea229c93b0d1b5db913 Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Mon, 19 May 2025 13:01:52 -0600 Subject: [PATCH 02/28] rename installer functions correctly Signed-off-by: Travis Cotton --- src/layer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/layer.py b/src/layer.py index 68c46e6..a0afe94 100644 --- a/src/layer.py +++ b/src/layer.py @@ -106,7 +106,7 @@ def buildah_handler(line): # Copy Files try: - inst.install_base_copyfiles(copyfiles) + inst.install_copyfiles(copyfiles) except Exception as e: self.logger.error(f"Error running commands: {e}") cmd(["buildah","rm"] + [cname]) @@ -118,7 +118,7 @@ def buildah_handler(line): # Run Commands try: - inst.install_base_commands(commands) + inst.install_commands(commands) if os.path.islink(mname + '/etc/resolv.conf'): self.logger.info("removing resolv.conf link (this link breaks running a container)") os.unlink(mname + '/etc/resolv.conf') From 83b49e59f3941367743815bcc3425835ffffd62c Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Mon, 19 May 2025 13:22:16 -0600 Subject: [PATCH 03/28] fix error where package manager is put before the container name Signed-off-by: Travis Cotton --- src/installer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/installer.py b/src/installer.py index 5ac55a7..ad2fed3 100644 --- a/src/installer.py +++ b/src/installer.py @@ -226,7 +226,7 @@ def install_repos(self, repos, proxy): pargs = ['config-manager', '--save', '--setopt=*.proxy=',proxy, repo_name] args = [self.cname, '--', 'bash', '-c', self.pkg_man] + pargs - rc = cmd(["buildah","run"] + [self.pkg_man] + args) + rc = cmd(["buildah","run"] + args) if rc != 0: raise Exception("Failed to set proxy for repo", r['alias'], r['url'], proxy) From 640bf918b1b8eac14a8f431970c76a7ecdd84520 Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Mon, 19 May 2025 13:26:08 -0600 Subject: [PATCH 04/28] rinse and repeat the previous step... Signed-off-by: Travis Cotton --- src/installer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/installer.py b/src/installer.py index ad2fed3..0e334fe 100644 --- a/src/installer.py +++ b/src/installer.py @@ -212,7 +212,7 @@ def install_repos(self, repos, proxy): rargs = ['config-manager','--save','--add-repo', r['url']] args = [self.cname, '--', 'bash', '-c', self.pkg_man] + rargs - rc = cmd(["buildah","run"] + [self.pkg_man] + args) + rc = cmd(["buildah","run"] + args) if rc != 0: raise Exception("Failed to install repo", r['alias'], r['url']) # Set Proxy if using DNF From 8e510e86d2a83113b9b77c4516c8d5773e4e7d5a Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Mon, 19 May 2025 13:28:42 -0600 Subject: [PATCH 05/28] tell GPG add commands to use the container... Signed-off-by: Travis Cotton --- src/installer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/installer.py b/src/installer.py index 0e334fe..1610649 100644 --- a/src/installer.py +++ b/src/installer.py @@ -232,7 +232,7 @@ def install_repos(self, repos, proxy): if "gpg" in r: # Using rpm apparently works for both Yum- and Zypper-based distros. - gargs = ['rpm', '--import', r['gpg']] + gargs = [self.cname, '--', 'bash', '-c', 'rpm', '--import', r['gpg']] if proxy != "": arg_env = os.environ.copy() arg_env['https_proxy'] = proxy From c0894c955135c9f94d8c3a8bb1dbcc1a6f4fd9f4 Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Mon, 19 May 2025 15:24:55 -0600 Subject: [PATCH 06/28] working through buildah run errors Signed-off-by: Travis Cotton --- src/installer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/installer.py b/src/installer.py index 1610649..b55b7bc 100644 --- a/src/installer.py +++ b/src/installer.py @@ -232,7 +232,7 @@ def install_repos(self, repos, proxy): if "gpg" in r: # Using rpm apparently works for both Yum- and Zypper-based distros. - gargs = [self.cname, '--', 'bash', '-c', 'rpm', '--import', r['gpg']] + gargs = [self.cname, '--', 'bash', '-c', 'rpm --import ' + r['gpg']] if proxy != "": arg_env = os.environ.copy() arg_env['https_proxy'] = proxy @@ -257,7 +257,7 @@ def install_package_groups(self, package_groups): logging.info("\n".join(package_groups)) if self.pkg_man == "zypper": logging.warn("zypper does not support package groups") - args = [self.cname, '--', 'bash', '-c', self.pkg_man] + package_groups + args = [self.cname, '--', 'bash', '-c', self.pkg_man, 'groupinstall'] + package_groups cmd(["buildah","run"] + args) def remove_packages(self, remove_packages): From d7a8c5356c23feaf226f66130ae052ee96d459f6 Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Mon, 19 May 2025 15:53:02 -0600 Subject: [PATCH 07/28] fixing buildah run command format Signed-off-by: Travis Cotton --- src/installer.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/installer.py b/src/installer.py index b55b7bc..90a02bc 100644 --- a/src/installer.py +++ b/src/installer.py @@ -207,11 +207,11 @@ def install_repos(self, repos, proxy): priority = r['priority'] else: priority = 99 - rargs = ['addrepo','-f','-p', priority, r['url'], r['alias']] + rargs = ' addrepo -f -p ' + priority + ' ' + r['url'] + ' ' + r['alias'] elif self.pkg_man == "dnf": - rargs = ['config-manager','--save','--add-repo', r['url']] + rargs = ' config-manager --save --add-repo ' + r['url'] - args = [self.cname, '--', 'bash', '-c', self.pkg_man] + rargs + args = [self.cname, '--', 'bash', '-c', self.pkg_man + rargs] rc = cmd(["buildah","run"] + args) if rc != 0: raise Exception("Failed to install repo", r['alias'], r['url']) @@ -223,9 +223,9 @@ def install_repos(self, repos, proxy): repo_name = r['url'].split('https://')[1].replace('/','_') elif r['url'].startswith('http'): repo_name = r['url'].split('http://')[1].replace('/','_') - pargs = ['config-manager', '--save', '--setopt=*.proxy=',proxy, repo_name] + pargs = ' config-manager --save --setopt=*.proxy= ' + proxy + ' ' + repo_name - args = [self.cname, '--', 'bash', '-c', self.pkg_man] + pargs + args = [self.cname, '--', 'bash', '-c', self.pkg_man + pargs] rc = cmd(["buildah","run"] + args) if rc != 0: raise Exception("Failed to set proxy for repo", r['alias'], r['url'], proxy) @@ -246,7 +246,7 @@ def install_packages(self, packages): return logging.info(f"PACKAGES: Installing these packages to {self.cname}") logging.info("\n".join(packages)) - args = [self.cname, '--', 'bash', '-c', self.pkg_man, 'install'] + packages + args = [self.cname, '--', 'bash', '-c', self.pkg_man + ' install ' + " ".join(packages)] cmd(["buildah","run"] + args) def install_package_groups(self, package_groups): @@ -257,7 +257,7 @@ def install_package_groups(self, package_groups): logging.info("\n".join(package_groups)) if self.pkg_man == "zypper": logging.warn("zypper does not support package groups") - args = [self.cname, '--', 'bash', '-c', self.pkg_man, 'groupinstall'] + package_groups + args = [self.cname, '--', 'bash', '-c', self.pkg_man + ' groupinstall ' + " ".join(package_groups)] cmd(["buildah","run"] + args) def remove_packages(self, remove_packages): From 4ffbfdac809fbcc5ccd0c40805faab3358c01f93 Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Tue, 20 May 2025 10:08:46 -0600 Subject: [PATCH 08/28] add gpgcheck option Signed-off-by: Travis Cotton --- src/arguments.py | 1 + src/image-build | 1 + src/installer.py | 16 +++++++++++++--- src/layer.py | 6 +++--- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/arguments.py b/src/arguments.py index a96ef91..34e7338 100644 --- a/src/arguments.py +++ b/src/arguments.py @@ -22,6 +22,7 @@ def process_args(terminal_args, config_options): processed_args['pkg_man'] = terminal_args.pkg_man or config_options.get('pkg_manager') if not processed_args['pkg_man']: raise ValueError("'pkg_man' required when 'layer_type' is base") + processed_args['gpgcheck'] = terminal_args.gpgcheck or config_options.get('gpgcheck') elif processed_args['layer_type'] == "ansible": processed_args['ansible_groups'] = terminal_args.group_list or config_options.get('groups', []) processed_args['ansible_pb'] = terminal_args.pb or config_options.get('playbooks', []) diff --git a/src/image-build b/src/image-build index 023f5cf..da50852 100755 --- a/src/image-build +++ b/src/image-build @@ -35,6 +35,7 @@ def main(): parser.add_argument('--config', type=str, required=True, help='Configuration file is required') parser.add_argument('--repo', type=str, required=False) parser.add_argument('--pkg-manager', dest="pkg_man", type=str, required=False) + parser.add_argument('--gpgcheck', dest="gpgcheck", type=bool, required=False) parser.add_argument('--groups', dest='group_list', action='store', nargs='+', type=str, default=[], help='List of groups') parser.add_argument('--vars', dest='vars', action='store', nargs='+', type=str, default=[], help='List of variables') parser.add_argument('--pb', type=str) diff --git a/src/installer.py b/src/installer.py index 90a02bc..32b3112 100644 --- a/src/installer.py +++ b/src/installer.py @@ -7,10 +7,11 @@ from utils import cmd class Installer: - def __init__(self, pkg_man, cname, mname): + def __init__(self, pkg_man, cname, mname, gpgcheck=True): self.pkg_man = pkg_man self.cname = cname self.mname = mname + self.gpgcheck = gpgcheck # Create temporary directory for logs, cache, etc. for package manager os.makedirs(os.path.join(mname, "tmp"), exist_ok=True) @@ -246,7 +247,13 @@ def install_packages(self, packages): return logging.info(f"PACKAGES: Installing these packages to {self.cname}") logging.info("\n".join(packages)) - args = [self.cname, '--', 'bash', '-c', self.pkg_man + ' install ' + " ".join(packages)] + args = [self.cname, '--', 'bash', '-c', self.pkg_man + ' install '] + if self.gpgcheck is not True: + if self.pkg_man == 'dnf': + args.append('--nogpgcheck') + elif self.pkg_man == 'zypper': + args.append('--no-gpg-checks') + args.append(" ".join(packages)) cmd(["buildah","run"] + args) def install_package_groups(self, package_groups): @@ -255,9 +262,12 @@ def install_package_groups(self, package_groups): return logging.info(f"PACKAGES: Installing these package groups to {self.cname}") logging.info("\n".join(package_groups)) + args = [self.cname, '--', 'bash', '-c', self.pkg_man + ' groupinstall '] if self.pkg_man == "zypper": logging.warn("zypper does not support package groups") - args = [self.cname, '--', 'bash', '-c', self.pkg_man + ' groupinstall ' + " ".join(package_groups)] + if self.gpgcheck is not True: + args.append('--nogpgcheck') + args.append(" ".join(package_groups)) cmd(["buildah","run"] + args) def remove_packages(self, remove_packages): diff --git a/src/layer.py b/src/layer.py index a0afe94..df6498c 100644 --- a/src/layer.py +++ b/src/layer.py @@ -90,7 +90,7 @@ def buildah_handler(line): # Packages inst.install_scratch_packages(packages, repo_dest, proxy) else: - inst.install_modules(modules, repo_dest, self.args['proxy']) + #inst.install_modules(modules, repo_dest, self.args['proxy']) inst.install_package_groups(package_groups, repo_dest, proxy) inst.install_packages(packages, repo_dest, proxy) # Remove Packages @@ -106,7 +106,7 @@ def buildah_handler(line): # Copy Files try: - inst.install_copyfiles(copyfiles) + inst.install_base_copyfiles(copyfiles) except Exception as e: self.logger.error(f"Error running commands: {e}") cmd(["buildah","rm"] + [cname]) @@ -118,7 +118,7 @@ def buildah_handler(line): # Run Commands try: - inst.install_commands(commands) + inst.install_base_commands(commands) if os.path.islink(mname + '/etc/resolv.conf'): self.logger.info("removing resolv.conf link (this link breaks running a container)") os.unlink(mname + '/etc/resolv.conf') From ede02a08327fedcfdce6a548d8102501cc2f489a Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Tue, 20 May 2025 12:55:26 -0600 Subject: [PATCH 09/28] fix weird buildah formatting issue Signed-off-by: Travis Cotton --- src/installer.py | 16 +++++++++------- src/layer.py | 6 +++++- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/installer.py b/src/installer.py index 32b3112..b807190 100644 --- a/src/installer.py +++ b/src/installer.py @@ -247,13 +247,14 @@ def install_packages(self, packages): return logging.info(f"PACKAGES: Installing these packages to {self.cname}") logging.info("\n".join(packages)) - args = [self.cname, '--', 'bash', '-c', self.pkg_man + ' install '] + args = [self.cname, '--', 'bash', '-c'] + pkg_cmd = [self.pkg_man, 'install', '-y'] if self.gpgcheck is not True: if self.pkg_man == 'dnf': - args.append('--nogpgcheck') + pkg_cmd.append('--nogpgcheck') elif self.pkg_man == 'zypper': - args.append('--no-gpg-checks') - args.append(" ".join(packages)) + pkg_cmd.append('--no-gpg-checks') + args.append(" ".join(pkg_cmd + packages)) cmd(["buildah","run"] + args) def install_package_groups(self, package_groups): @@ -262,12 +263,13 @@ def install_package_groups(self, package_groups): return logging.info(f"PACKAGES: Installing these package groups to {self.cname}") logging.info("\n".join(package_groups)) - args = [self.cname, '--', 'bash', '-c', self.pkg_man + ' groupinstall '] + args = [self.cname, '--', 'bash', '-c'] + pkg_cmd = [self.pkg_man, 'groupinstall', '-y'] if self.pkg_man == "zypper": logging.warn("zypper does not support package groups") if self.gpgcheck is not True: - args.append('--nogpgcheck') - args.append(" ".join(package_groups)) + pkg_cmd.append('--nogpgcheck') + args.append(" ".join(pkg_cmd + package_groups)) cmd(["buildah","run"] + args) def remove_packages(self, remove_packages): diff --git a/src/layer.py b/src/layer.py index df6498c..98cfa6e 100644 --- a/src/layer.py +++ b/src/layer.py @@ -23,6 +23,10 @@ def _build_base(self, repos, modules, packages, package_groups, remove_packages, container = self.args['name'] registry_opts_pull = self.args['registry_opts_pull'] package_manager = self.args['pkg_man'] + if 'gpgcheck' in self.args: + gpgcheck = self.args['gpgcheck'] + else: + gpgcheck = True if 'proxy' in self.args: proxy = self.args['proxy'] else: @@ -55,7 +59,7 @@ def buildah_handler(line): inst = None try: - inst = installer.Installer(package_manager, cname, mname) + inst = installer.Installer(package_manager, cname, mname, gpgcheck) except Exception as e: self.logger.error(f"Error preparing installer: {e}") cmd(["buildah","rm"] + [cname]) From 23b9a2b79a1edeb18b2d9f8f02695bb28b82029e Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Tue, 20 May 2025 13:22:01 -0600 Subject: [PATCH 10/28] handle package group names with spaces properly Signed-off-by: Travis Cotton --- src/installer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/installer.py b/src/installer.py index b807190..4d6771f 100644 --- a/src/installer.py +++ b/src/installer.py @@ -269,7 +269,7 @@ def install_package_groups(self, package_groups): logging.warn("zypper does not support package groups") if self.gpgcheck is not True: pkg_cmd.append('--nogpgcheck') - args.append(" ".join(pkg_cmd + package_groups)) + args.append(" ".join(pkg_cmd + [f'"{pg}"' for pg in package_groups])) cmd(["buildah","run"] + args) def remove_packages(self, remove_packages): From 8f671b0bced4b62a6d163f3c4fff75b34b354e47 Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Wed, 21 May 2025 07:17:45 -0600 Subject: [PATCH 11/28] cosmetic changes... Signed-off-by: Travis Cotton --- src/installer.py | 4 ++-- src/layer.py | 1 - src/publish.py | 2 -- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/installer.py b/src/installer.py index 4d6771f..b5a0503 100644 --- a/src/installer.py +++ b/src/installer.py @@ -306,7 +306,7 @@ def install_commands(self, commands): loglevel = logging.error else: loglevel = logging.error - out = cmd(build_cmd + args, stderr_handler=loglevel) + cmd(["buildah","run"] + args, stderr_handler=loglevel) def install_copyfiles(self, copyfiles): if len(copyfiles) == 0: @@ -320,4 +320,4 @@ def install_copyfiles(self, copyfiles): args.extend(o.split()) logging.info(f['src'] + ' -> ' + f['dest']) args += [ self.cname, f['src'], f['dest'] ] - out=cmd(["buildah","copy"] + args) + cmd(["buildah","copy"] + args) diff --git a/src/layer.py b/src/layer.py index 98cfa6e..459f8ae 100644 --- a/src/layer.py +++ b/src/layer.py @@ -94,7 +94,6 @@ def buildah_handler(line): # Packages inst.install_scratch_packages(packages, repo_dest, proxy) else: - #inst.install_modules(modules, repo_dest, self.args['proxy']) inst.install_package_groups(package_groups, repo_dest, proxy) inst.install_packages(packages, repo_dest, proxy) # Remove Packages diff --git a/src/publish.py b/src/publish.py index 3c862e1..ffff8ee 100644 --- a/src/publish.py +++ b/src/publish.py @@ -121,7 +121,6 @@ def squash_image(mname, tmpdir): # print(process.stdout) def s3_push(cname, layer_name, credentials, publish_tags, s3_prefix, s3_bucket): - def buildah_handler(line): out.append(line) out = [] @@ -177,7 +176,6 @@ def buildah_handler(line): push_file(tmpdir + '/rootfs', image_name, s3, s3_bucket) def registry_push(layer_name, registry_opts, publish_tags, registry_endpoint): - image_name = layer_name+':'+publish_tags print("pushing layer " + layer_name + " to " + registry_endpoint +'/'+image_name) args = registry_opts + [image_name, registry_endpoint +'/'+image_name] From 04935069857c793c90bd939c293e76115ba99987 Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Wed, 21 May 2025 08:26:21 -0600 Subject: [PATCH 12/28] fix gpg check flags order; set default value to False Signed-off-by: Travis Cotton --- src/arguments.py | 2 +- src/installer.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/arguments.py b/src/arguments.py index 34e7338..125013a 100644 --- a/src/arguments.py +++ b/src/arguments.py @@ -22,7 +22,7 @@ def process_args(terminal_args, config_options): processed_args['pkg_man'] = terminal_args.pkg_man or config_options.get('pkg_manager') if not processed_args['pkg_man']: raise ValueError("'pkg_man' required when 'layer_type' is base") - processed_args['gpgcheck'] = terminal_args.gpgcheck or config_options.get('gpgcheck') + processed_args['gpgcheck'] = terminal_args.gpgcheck or config_options.get('gpgcheck', False) elif processed_args['layer_type'] == "ansible": processed_args['ansible_groups'] = terminal_args.group_list or config_options.get('groups', []) processed_args['ansible_pb'] = terminal_args.pb or config_options.get('playbooks', []) diff --git a/src/installer.py b/src/installer.py index b5a0503..e287945 100644 --- a/src/installer.py +++ b/src/installer.py @@ -248,13 +248,13 @@ def install_packages(self, packages): logging.info(f"PACKAGES: Installing these packages to {self.cname}") logging.info("\n".join(packages)) args = [self.cname, '--', 'bash', '-c'] - pkg_cmd = [self.pkg_man, 'install', '-y'] + pkg_cmd = [self.pkg_man] if self.gpgcheck is not True: if self.pkg_man == 'dnf': pkg_cmd.append('--nogpgcheck') elif self.pkg_man == 'zypper': pkg_cmd.append('--no-gpg-checks') - args.append(" ".join(pkg_cmd + packages)) + args.append(" ".join(pkg_cmd + [ 'install', '-y'] + packages)) cmd(["buildah","run"] + args) def install_package_groups(self, package_groups): From 761ba5b5e1ebcd86bf73a1c38ba1bce6317a533d Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Wed, 21 May 2025 08:37:28 -0600 Subject: [PATCH 13/28] actually set the default gpg check value to true... Signed-off-by: Travis Cotton --- src/arguments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arguments.py b/src/arguments.py index 125013a..82e23ae 100644 --- a/src/arguments.py +++ b/src/arguments.py @@ -22,7 +22,7 @@ def process_args(terminal_args, config_options): processed_args['pkg_man'] = terminal_args.pkg_man or config_options.get('pkg_manager') if not processed_args['pkg_man']: raise ValueError("'pkg_man' required when 'layer_type' is base") - processed_args['gpgcheck'] = terminal_args.gpgcheck or config_options.get('gpgcheck', False) + processed_args['gpgcheck'] = terminal_args.gpgcheck or config_options.get('gpgcheck', True) elif processed_args['layer_type'] == "ansible": processed_args['ansible_groups'] = terminal_args.group_list or config_options.get('groups', []) processed_args['ansible_pb'] = terminal_args.pb or config_options.get('playbooks', []) From a8bcddc570522410c218d241ac7bb351816b2213 Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Wed, 21 May 2025 14:17:58 -0600 Subject: [PATCH 14/28] increase subuid/subgid Signed-off-by: Travis Cotton --- dockerfiles/dnf/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dockerfiles/dnf/Dockerfile b/dockerfiles/dnf/Dockerfile index 361aa6d..59d1571 100644 --- a/dockerfiles/dnf/Dockerfile +++ b/dockerfiles/dnf/Dockerfile @@ -27,8 +27,8 @@ RUN setcap cap_setuid=ep "$(command -v newuidmap)" && \ chmod 0755 "$(command -v newuidmap)" && \ chmod 0755 "$(command -v newgidmap)" && \ rpm --restore shadow-utils && \ - echo "builder:2000:50000" > /etc/subuid && \ - echo "builder:2000:50000" > /etc/subgid + echo "builder:2000:65534" > /etc/subuid && \ + echo "builder:2000:65534" > /etc/subgid # Create local user for rootless image builds RUN useradd --uid 1002 builder && \ From 0bf166eab7237a708029fe134c733c12a96fab65 Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Wed, 4 Jun 2025 14:35:27 -0600 Subject: [PATCH 15/28] reconfig how container gets built and how the build is run inside the build container to work better with subuid/subgid mapping Signed-off-by: Travis Cotton --- dockerfiles/dnf/Dockerfile | 14 +++++--------- dockerfiles/dnf/Dockerfile.minimal | 14 +++++--------- entrypoint.sh | 14 ++++++++++++++ tests/dnf/rocky9_scratch.yaml | 24 ++++++++++++++++++++++++ tests/zypper/suse_bci_base.yaml | 15 +++++++++++++++ 5 files changed, 63 insertions(+), 18 deletions(-) create mode 100644 entrypoint.sh create mode 100644 tests/dnf/rocky9_scratch.yaml create mode 100644 tests/zypper/suse_bci_base.yaml diff --git a/dockerfiles/dnf/Dockerfile b/dockerfiles/dnf/Dockerfile index 59d1571..3928a75 100644 --- a/dockerfiles/dnf/Dockerfile +++ b/dockerfiles/dnf/Dockerfile @@ -21,23 +21,19 @@ RUN pip3.11 install -r /requirements.txt COPY src/ /usr/local/bin/ RUN chmod -R 0755 /usr/local/bin/ +COPY entrypoint.sh /entrypoint.sh + # Allow non-root to run buildah commands RUN setcap cap_setuid=ep "$(command -v newuidmap)" && \ setcap cap_setgid=ep "$(command -v newgidmap)" &&\ chmod 0755 "$(command -v newuidmap)" && \ chmod 0755 "$(command -v newgidmap)" && \ - rpm --restore shadow-utils && \ - echo "builder:2000:65534" > /etc/subuid && \ - echo "builder:2000:65534" > /etc/subgid + rpm --restore shadow-utils # Create local user for rootless image builds -RUN useradd --uid 1002 builder && \ +RUN useradd --uid 1000 builder && \ chown -R builder /home/builder -# Make builder the default user when running container -USER builder -WORKDIR /home/builder - ENV BUILDAH_ISOLATION=chroot -ENTRYPOINT ["/usr/bin/buildah", "unshare"] +ENTRYPOINT ["/entrypoint.sh"] diff --git a/dockerfiles/dnf/Dockerfile.minimal b/dockerfiles/dnf/Dockerfile.minimal index 955cad8..d686705 100644 --- a/dockerfiles/dnf/Dockerfile.minimal +++ b/dockerfiles/dnf/Dockerfile.minimal @@ -15,10 +15,10 @@ RUN microdnf install -y \ microdnf clean all # Create local user for rootless image builds -RUN echo "builder:x:1002:1002::/home/builder:/bin/bash" >> /etc/passwd && \ - echo "builder:x:1002:" >> /etc/group && \ +RUN echo "builder:x:1000:1000::/home/builder:/bin/bash" >> /etc/passwd && \ + echo "builder:x:1000:" >> /etc/group && \ mkdir -p /home/builder && \ - chown -R 1002:1002 /home/builder + chown -R 1000:1000 /home/builder # Add our custom scripts COPY src/ /usr/local/bin/ @@ -28,20 +28,16 @@ RUN chmod -R 0755 /usr/local/bin/ RUN setcap cap_setuid=ep "$(command -v newuidmap)" && \ setcap cap_setgid=ep "$(command -v newgidmap)" && \ chmod 0755 "$(command -v newuidmap)" && \ - chmod 0755 "$(command -v newgidmap)" && \ - echo "builder:2000:50000" > /etc/subuid && \ - echo "builder:2000:50000" > /etc/subgid + chmod 0755 "$(command -v newgidmap)" # Set up environment variables ENV BUILDAH_ISOLATION=chroot # Switch to non-root user -USER builder # Verify Python functionality RUN python3.11 -m pip install --no-cache-dir --upgrade pip && \ python3.11 -m pip install --no-cache-dir PyYAML ansible==11.1.0 ansible-base ansible-bender boto3 dnspython requests jinja2_ansible_filters -WORKDIR /home/builder # Default entrypoint -ENTRYPOINT ["/usr/bin/buildah", "unshare"] +ENTRYPOINT ["/entrypoint.sh"] diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..58dcdef --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +if [[ ! -v USERNS_RANGE ]] +then + USERNS_RANGE=64536 +fi + + +echo "builder:1001:${USERNS_RANGE}" > /etc/subuid +echo "builder:1001:${USERNS_RANGE}" > /etc/subgid + +chown -R builder /home/builder + +exec su builder -c "buildah unshare $*" diff --git a/tests/dnf/rocky9_scratch.yaml b/tests/dnf/rocky9_scratch.yaml new file mode 100644 index 0000000..da776d0 --- /dev/null +++ b/tests/dnf/rocky9_scratch.yaml @@ -0,0 +1,24 @@ +# build a test rocky9 based image from scratch +options: + layer_type: 'base' + name: 'rocky-base' + publish_tags: '9.5' + pkg_manager: 'dnf' + parent: 'scratch' + publish_local: true + +repos: + - alias: 'Rocky_9_BaseOS' + url: 'https://dl.rockylinux.org/pub/rocky/9/BaseOS/x86_64/os/' + gpg: 'https://dl.rockylinux.org/pub/rocky/RPM-GPG-KEY-Rocky-9' + - alias: 'Rocky_9_AppStream' + url: 'https://dl.rockylinux.org/pub/rocky/9/AppStream/x86_64/os/' + gpg: 'https://dl.rockylinux.org/pub/rocky/RPM-GPG-KEY-Rocky-9' + +package_groups: + - 'Minimal Install' + +packages: + - kernel + - wget + - dracut-live diff --git a/tests/zypper/suse_bci_base.yaml b/tests/zypper/suse_bci_base.yaml new file mode 100644 index 0000000..da4044a --- /dev/null +++ b/tests/zypper/suse_bci_base.yaml @@ -0,0 +1,15 @@ +# Build a a layer importing from bci-base +options: + layer_type: 'base' + name: 'suse-base' + publish_tags: + - '15.7' + pkg_manager: 'zypper' + parent: 'registry.suse.com/bci/bci-base:15.7' + publish_local: true + +packages: + - cloud-init + - python3 + - vim + - chrony From c2d6d9198ecb2dd191741857038e55288e66145f Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Wed, 4 Jun 2025 14:54:06 -0600 Subject: [PATCH 16/28] make entrypoint executable Signed-off-by: Travis Cotton --- entrypoint.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 entrypoint.sh diff --git a/entrypoint.sh b/entrypoint.sh old mode 100644 new mode 100755 From be6482bb3582af0c9ba24dd37ac994a64c3a1ff8 Mon Sep 17 00:00:00 2001 From: Alex Lovell-Troy Date: Sun, 8 Jun 2025 09:54:38 +0200 Subject: [PATCH 17/28] Attempting additional permission adjustments for uidmap/gidmap Signed-off-by: Travis Cotton --- dockerfiles/dnf/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/dockerfiles/dnf/Dockerfile b/dockerfiles/dnf/Dockerfile index 3928a75..5c2ccef 100644 --- a/dockerfiles/dnf/Dockerfile +++ b/dockerfiles/dnf/Dockerfile @@ -24,6 +24,7 @@ RUN chmod -R 0755 /usr/local/bin/ COPY entrypoint.sh /entrypoint.sh # Allow non-root to run buildah commands +RUN chmod u+s $(command -v newuidmap) $(command -v newgidmap) RUN setcap cap_setuid=ep "$(command -v newuidmap)" && \ setcap cap_setgid=ep "$(command -v newgidmap)" &&\ chmod 0755 "$(command -v newuidmap)" && \ From 9679b3495a9b25c5dd21bdf1c5826db19f19ed26 Mon Sep 17 00:00:00 2001 From: Alex Lovell-Troy Date: Sun, 8 Jun 2025 10:09:29 +0200 Subject: [PATCH 18/28] Update entrypoint to expect running as normal user Signed-off-by: Travis Cotton --- README.md | 13 ++++++++++--- entrypoint.sh | 18 ++++++++++-------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index e6a0e00..cf42755 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,20 @@ The recommended and official way to run `image-build` is using the `ghcr.io/open To build an image using the container, the config file needs to be mapped into the container, as well as the FUSE filesystem device: ``` -podman run \ - --rm \ +podman run --rm \ --device /dev/fuse \ --userns keep-id:uid=1002,gid=1002 \ -v /path/to/config.yaml:/home/builder/config.yaml \ + --network host \ + --cap-add=SYS_ADMIN \ + --cap-add=SETUID \ + --cap-add=SETGID \ + --security-opt seccomp=unconfined \ + --security-opt label=disable \ + --userns=keep-id \ + -v /opt/workdir/images/test-rocky-9.5.yaml:/home/builder/config.yaml \ ghcr.io/openchami/image-build:latest \ - image-build --config config.yaml + image-build --config config.yaml --log-level DEBUG ``` If you are building EL9 images, use the `ghcr.io/openchami/image-build-el9:latest` image. diff --git a/entrypoint.sh b/entrypoint.sh index 58dcdef..350ac81 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,14 +1,16 @@ #!/bin/bash +set -euo pipefail -if [[ ! -v USERNS_RANGE ]] -then - USERNS_RANGE=64536 +# Only do this if you really need to override inside-container subuid/gid behavior +# Usually not needed unless you're testing mapping features +if [[ -w /etc/subuid && -w /etc/subgid ]]; then + echo "builder:1001:${USERNS_RANGE:-65536}" > /etc/subuid + echo "builder:1001:${USERNS_RANGE:-65536}" > /etc/subgid fi +# Make sure builder owns its homedir (optional if baked into image) +chown -R builder /home/builder || true -echo "builder:1001:${USERNS_RANGE}" > /etc/subuid -echo "builder:1001:${USERNS_RANGE}" > /etc/subgid +# Run buildah directly +exec buildah unshare "$@" -chown -R builder /home/builder - -exec su builder -c "buildah unshare $*" From 23b8f85640cf61a1e46f21df1c754197f074d7f5 Mon Sep 17 00:00:00 2001 From: Alex Lovell-Troy Date: Sun, 8 Jun 2025 10:23:15 +0200 Subject: [PATCH 19/28] simplify entrypoint for both root and non-root invocation Signed-off-by: Travis Cotton --- README.md | 1 - entrypoint.sh | 19 ++++++------------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index cf42755..058b88e 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,6 @@ podman run --rm \ --cap-add=SETGID \ --security-opt seccomp=unconfined \ --security-opt label=disable \ - --userns=keep-id \ -v /opt/workdir/images/test-rocky-9.5.yaml:/home/builder/config.yaml \ ghcr.io/openchami/image-build:latest \ image-build --config config.yaml --log-level DEBUG diff --git a/entrypoint.sh b/entrypoint.sh index 350ac81..968fcd6 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,16 +1,9 @@ #!/bin/bash set -euo pipefail -# Only do this if you really need to override inside-container subuid/gid behavior -# Usually not needed unless you're testing mapping features -if [[ -w /etc/subuid && -w /etc/subgid ]]; then - echo "builder:1001:${USERNS_RANGE:-65536}" > /etc/subuid - echo "builder:1001:${USERNS_RANGE:-65536}" > /etc/subgid -fi - -# Make sure builder owns its homedir (optional if baked into image) -chown -R builder /home/builder || true - -# Run buildah directly -exec buildah unshare "$@" - +# Only su if running as root +if [[ "$(id -u)" == "0" ]]; then + exec su - builder -c "buildah unshare $*" +else + exec buildah unshare "$@" +fi \ No newline at end of file From 4840e8fcf731e6d8bb59a5a56251a838d07e738a Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Sun, 13 Jul 2025 09:36:20 -0600 Subject: [PATCH 20/28] Revert "make entrypoint executable" This reverts commit 3b6813d83ee237c546f778d2015053b06e48b67f. Signed-off-by: Travis Cotton --- entrypoint.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 entrypoint.sh diff --git a/entrypoint.sh b/entrypoint.sh old mode 100755 new mode 100644 From ddd5959d5d54df210a9a359158e29c39044c2afd Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Sun, 13 Jul 2025 10:21:26 -0600 Subject: [PATCH 21/28] signoff working? Signed-off-by: Travis Cotton --- tests/dnf/rocky9_scratch.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/dnf/rocky9_scratch.yaml b/tests/dnf/rocky9_scratch.yaml index da776d0..b7a3961 100644 --- a/tests/dnf/rocky9_scratch.yaml +++ b/tests/dnf/rocky9_scratch.yaml @@ -1,4 +1,5 @@ # build a test rocky9 based image from scratch + options: layer_type: 'base' name: 'rocky-base' From 9283d725f336da79eea599038591d3cda0368168 Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Sun, 13 Jul 2025 10:28:02 -0600 Subject: [PATCH 22/28] Revert "Attempting additional permission adjustments for uidmap/gidmap" This reverts commit 5e67b00cb93ea40961430b072bd9ed6227232627. Signed-off-by: Travis Cotton --- dockerfiles/dnf/Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/dockerfiles/dnf/Dockerfile b/dockerfiles/dnf/Dockerfile index 5c2ccef..3928a75 100644 --- a/dockerfiles/dnf/Dockerfile +++ b/dockerfiles/dnf/Dockerfile @@ -24,7 +24,6 @@ RUN chmod -R 0755 /usr/local/bin/ COPY entrypoint.sh /entrypoint.sh # Allow non-root to run buildah commands -RUN chmod u+s $(command -v newuidmap) $(command -v newgidmap) RUN setcap cap_setuid=ep "$(command -v newuidmap)" && \ setcap cap_setgid=ep "$(command -v newgidmap)" &&\ chmod 0755 "$(command -v newuidmap)" && \ From ae92a56ff7b84d26f310dae396c54d36de85c976 Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Sun, 13 Jul 2025 10:28:12 -0600 Subject: [PATCH 23/28] Revert "simplify entrypoint for both root and non-root invocation" This reverts commit eba24b25bc17f1d322a5437e9e28973f700b9358. Signed-off-by: Travis Cotton --- README.md | 1 + entrypoint.sh | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 058b88e..cf42755 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ podman run --rm \ --cap-add=SETGID \ --security-opt seccomp=unconfined \ --security-opt label=disable \ + --userns=keep-id \ -v /opt/workdir/images/test-rocky-9.5.yaml:/home/builder/config.yaml \ ghcr.io/openchami/image-build:latest \ image-build --config config.yaml --log-level DEBUG diff --git a/entrypoint.sh b/entrypoint.sh index 968fcd6..350ac81 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,9 +1,16 @@ #!/bin/bash set -euo pipefail -# Only su if running as root -if [[ "$(id -u)" == "0" ]]; then - exec su - builder -c "buildah unshare $*" -else - exec buildah unshare "$@" -fi \ No newline at end of file +# Only do this if you really need to override inside-container subuid/gid behavior +# Usually not needed unless you're testing mapping features +if [[ -w /etc/subuid && -w /etc/subgid ]]; then + echo "builder:1001:${USERNS_RANGE:-65536}" > /etc/subuid + echo "builder:1001:${USERNS_RANGE:-65536}" > /etc/subgid +fi + +# Make sure builder owns its homedir (optional if baked into image) +chown -R builder /home/builder || true + +# Run buildah directly +exec buildah unshare "$@" + From 2d960dcd9ae34266c1aef33781596ade3d254f84 Mon Sep 17 00:00:00 2001 From: Travis Cotton <80352065+travisbcotton@users.noreply.github.com> Date: Fri, 8 Aug 2025 08:11:52 -0600 Subject: [PATCH 24/28] Update dockerfiles/dnf/Dockerfile Co-authored-by: treydock Signed-off-by: Travis Cotton --- dockerfiles/dnf/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/dockerfiles/dnf/Dockerfile b/dockerfiles/dnf/Dockerfile index 3928a75..fddf134 100644 --- a/dockerfiles/dnf/Dockerfile +++ b/dockerfiles/dnf/Dockerfile @@ -22,6 +22,7 @@ COPY src/ /usr/local/bin/ RUN chmod -R 0755 /usr/local/bin/ COPY entrypoint.sh /entrypoint.sh +RUN chmod 0755 /entrypoint.sh # Allow non-root to run buildah commands RUN setcap cap_setuid=ep "$(command -v newuidmap)" && \ From 282388e3462e300cd26baf46b29b050cce6222fe Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Thu, 28 Aug 2025 15:35:31 -0600 Subject: [PATCH 25/28] update entrypoint to make subuid/gid rnage dynamoc Signed-off-by: Travis Cotton --- entrypoint.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index 350ac81..827e062 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,8 +1,6 @@ #!/bin/bash set -euo pipefail -# Only do this if you really need to override inside-container subuid/gid behavior -# Usually not needed unless you're testing mapping features if [[ -w /etc/subuid && -w /etc/subgid ]]; then echo "builder:1001:${USERNS_RANGE:-65536}" > /etc/subuid echo "builder:1001:${USERNS_RANGE:-65536}" > /etc/subgid @@ -12,5 +10,4 @@ fi chown -R builder /home/builder || true # Run buildah directly -exec buildah unshare "$@" - +exec su builder -c "buildah unshare $*" From 8f5fdfbf277b0e414c0b4143c9e60a98ba2f11f9 Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Thu, 28 Aug 2025 15:36:34 -0600 Subject: [PATCH 26/28] fix layer calling the correct copyfiles and runmd functions Signed-off-by: Travis Cotton --- src/layer.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/layer.py b/src/layer.py index 459f8ae..922d5c6 100644 --- a/src/layer.py +++ b/src/layer.py @@ -109,7 +109,7 @@ def buildah_handler(line): # Copy Files try: - inst.install_base_copyfiles(copyfiles) + inst.install_copyfiles(copyfiles) except Exception as e: self.logger.error(f"Error running commands: {e}") cmd(["buildah","rm"] + [cname]) @@ -121,7 +121,7 @@ def buildah_handler(line): # Run Commands try: - inst.install_base_commands(commands) + inst.install_commands(commands) if os.path.islink(mname + '/etc/resolv.conf'): self.logger.info("removing resolv.conf link (this link breaks running a container)") os.unlink(mname + '/etc/resolv.conf') @@ -204,4 +204,3 @@ def build_layer(self): # Publish the layer self.logger.info("Publishing Layer") publish(cname, self.args) - From 982d6e702f656f60269ab3bb251d65ab1bf1ed50 Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Wed, 17 Sep 2025 06:46:17 -0600 Subject: [PATCH 27/28] removing import of unused Setting python library Signed-off-by: Travis Cotton --- src/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.py b/src/utils.py index e9f290b..4d063f4 100644 --- a/src/utils.py +++ b/src/utils.py @@ -6,7 +6,7 @@ from ansible.executor.playbook_executor import PlaybookExecutor from ansible.playbook import Playbook from ansible.vars.manager import VariableManager -from ansible.config.manager import ConfigManager, Setting +from ansible.config.manager import ConfigManager from ansible.cli.config import ConfigCLI from ansible.parsing.yaml.objects import AnsibleVaultEncryptedUnicode From 5cabea7106717759990eab15f4e4a3b242a0c3d5 Mon Sep 17 00:00:00 2001 From: Travis Cotton Date: Mon, 22 Sep 2025 10:21:34 -0600 Subject: [PATCH 28/28] removing uneeded args to install_packages layer.py Signed-off-by: Travis Cotton --- src/layer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/layer.py b/src/layer.py index 922d5c6..7e772a5 100644 --- a/src/layer.py +++ b/src/layer.py @@ -94,8 +94,8 @@ def buildah_handler(line): # Packages inst.install_scratch_packages(packages, repo_dest, proxy) else: - inst.install_package_groups(package_groups, repo_dest, proxy) - inst.install_packages(packages, repo_dest, proxy) + inst.install_package_groups(package_groups) + inst.install_packages(packages) # Remove Packages inst.remove_packages(remove_packages) except Exception as e: