Skip to content

Commit

Permalink
Merge pull request #6094 from rvykydal/edns-certs-stage2-w-initramfs-r9
Browse files Browse the repository at this point in the history
Edns certs stage2 w initramfs r9
  • Loading branch information
jkonecny12 authored Jan 29, 2025
2 parents a09962d + 86e7913 commit 3c6eac9
Show file tree
Hide file tree
Showing 29 changed files with 1,019 additions and 12 deletions.
2 changes: 1 addition & 1 deletion anaconda.spec.in
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Source0: https://github.com/rhinstaller/%{name}/releases/download/%{name}-%{vers
%define libxklavierver 5.4
%define mehver 0.23-1
%define nmver 1.0
%define pykickstartver 3.32.11-1
%define pykickstartver 3.32.13-1
%define pypartedver 2.5-2
%define pythonblivetver 1:3.6.0-13
%define rpmver 4.10.0
Expand Down
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ AC_CONFIG_FILES([Makefile
pyanaconda/modules/boss/module_manager/Makefile
pyanaconda/modules/boss/user_interface/Makefile
pyanaconda/modules/security/Makefile
pyanaconda/modules/security/certificates/Makefile
pyanaconda/modules/timezone/Makefile
pyanaconda/modules/network/Makefile
pyanaconda/modules/network/firewall/Makefile
Expand Down
1 change: 1 addition & 0 deletions data/systemd/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dist_systemd_DATA = anaconda.service \
anaconda-sshd.service \
anaconda-nm-config.service \
anaconda-nm-disable-autocons.service \
anaconda-import-initramfs-certs.service \
anaconda-pre.service \
anaconda-fips.service

Expand Down
1 change: 1 addition & 0 deletions data/systemd/anaconda-generator
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ done
ln -sf $systemd_dir/anaconda-nm-config.service $target_dir/anaconda-nm-config.service
ln -sf $systemd_dir/anaconda-nm-disable-autocons.service $target_dir/anaconda-nm-disable-autocons.service
ln -sf $systemd_dir/anaconda-pre.service $target_dir/anaconda-pre.service
ln -sf "$systemd_dir/anaconda-import-initramfs-certs.service" "$target_dir/anaconda-import-initramfs-certs.service"
8 changes: 8 additions & 0 deletions data/systemd/anaconda-import-initramfs-certs.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[Unit]
Description=Import of certificates added in initramfs stage of Anaconda via kickstart
Before=NetworkManager.service
Before=anaconda.target

[Service]
Type=oneshot
ExecStart=/usr/libexec/anaconda/anaconda-import-initramfs-certs
45 changes: 45 additions & 0 deletions dracut/parse-kickstart
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ TMPDIR = "/tmp"
ARPHRD_ETHER = "1"
ARPHRD_INFINIBAND = "32"

CERT_TRANSPORT_DIR = "/run/install/certificates"

# Helper function for reading simple files in /sys
def readsysfile(f):
'''Return the contents of f, or "" if missing.'''
Expand Down Expand Up @@ -395,6 +397,48 @@ def ksnet_to_dracut(args, lineno, net, bootdev=False):

return " ".join(line)


def _dump_certificate(cert, root="/", dump_dir=None):
"""Dump the certificate into specified file."""
dump_dir = dump_dir or cert.dir
if not dump_dir:
log.error("Certificate destination is missing for %s", cert.filename)
return

dst_dir = os.path.join(root, dump_dir.lstrip('/'))
log.debug("Dumping certificate %s into %s.", cert.filename, dst_dir)
if not os.path.exists(dst_dir):
log.debug("Path %s for certificate does not exist, creating.", dst_dir)
os.makedirs(dst_dir)

dst = os.path.join(dst_dir, cert.filename)

if os.path.exists(dst):
log.warning("Certificate file %s already exists, replacing.", dst)

with open(dst, 'w') as f:
f.write(cert.cert)
f.write('\n')


def process_certificates(handler):
"""Import certificates defined in %certificate sections."""
for cert in handler.certificates:
log.info("Processing kickstart certificate %s", cert.filename)

if not cert.filename:
log.error("Missing certificate file name, skipping.")
continue

try:
_dump_certificate(cert)
except OSError as e:
log.error("Dump of certificate %s failed: %s", cert.filename, e)
continue
# Dump for transport to switchroot
_dump_certificate(cert, root=CERT_TRANSPORT_DIR+"/path/")


def process_kickstart(ksfile):
handler = DracutHandler()
try:
Expand All @@ -415,6 +459,7 @@ def process_kickstart(ksfile):
with open(TMPDIR+"/ks.info", "a") as f:
f.write('parsed_kickstart="%s"\n' % processed_file)
log.info("finished parsing kickstart")
process_certificates(handler)
return processed_file, handler.output

if __name__ == '__main__':
Expand Down
3 changes: 3 additions & 0 deletions pyanaconda/core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,3 +541,6 @@ class DisplayModes(Enum):

# A flag file indicated the installation is running in rescue mode
RESCUE_MODE_PATH = "/run/install/RESCUE_MODE"

# Installation phases
INSTALLATION_PHASE_PREINSTALL = "pre-install"
43 changes: 40 additions & 3 deletions pyanaconda/core/kickstart/specification.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,14 @@ class KickstartSpecification(object):
classes that represent them
sections - mapping of kickstart sections names to
classes that represent them
value is a class or a tuple (class, section_data_class)
where section_data_class is a value to be passed to dataObj
class argument (typically the corresponding sections_data class)
sections_data - mapping of kickstart sections data names to
classes that represent them
value is a class or a tuple (class, data_list_name)
where data_list_name is the name of the attribute holding
list of the section data objects of the class
addons - mapping of kickstart addons names to
classes that represent them
Expand All @@ -69,6 +75,25 @@ class NoKickstartSpecification(KickstartSpecification):
pass


class SectionDataListStrWrapper():
"""A wrapper for generating string from a list of kickstart data."""
def __init__(self, data_list, data):
"""Initializer.
:param data_list: list of section data objects
:param data: class required for the object to be included in the string
"""
self._data_list = data_list
self._data = data

def __str__(self):
retval = []
for data_obj in self._data_list:
if isinstance(data_obj, self._data):
retval.append(data_obj.__str__())
return "".join(retval)


class KickstartSpecificationHandler(KickstartHandler):
"""Handler defined by a kickstart specification."""

Expand All @@ -93,8 +118,16 @@ def __init__(self, specification):

def registerSectionData(self, name, data):
"""Register data used by a section."""
obj = data()
setattr(self, name, obj)
if isinstance(data, tuple):
# Multiple data objects (section instances) stored in a list
data, data_list_name = data
data_list = []
setattr(self, data_list_name, data_list)
obj = SectionDataListStrWrapper(data_list, data)
else:
# Single data object for all section instances
obj = data()
setattr(self, name, obj)
self._registerWriteOrder(obj)

def registerAddonData(self, name, data):
Expand All @@ -120,7 +153,11 @@ def __init__(self, handler, specification):
super().__init__(handler)

for section in specification.sections.values():
self.registerSection(section(handler))
if isinstance(section, tuple):
section_cls, data_obj = section
self.registerSection(section_cls(handler, dataObj=data_obj))
else:
self.registerSection(section(handler))

if specification.addons:
self.registerSection(AddonSection(handler))
Expand Down
18 changes: 17 additions & 1 deletion pyanaconda/installation.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
from pyanaconda.core.dbus import DBus
from pyanaconda.core.configuration.anaconda import conf
from pyanaconda.core.constants import PAYLOAD_LIVE_TYPES, PAYLOAD_TYPE_DNF
from pyanaconda.modules.common.constants.objects import BOOTLOADER, SNAPSHOT, FIREWALL
from pyanaconda.modules.common.constants.objects import BOOTLOADER, SNAPSHOT, FIREWALL, \
CERTIFICATES
from pyanaconda.modules.common.constants.services import STORAGE, USERS, SERVICES, NETWORK, \
SECURITY, LOCALIZATION, TIMEZONE, BOSS, SUBSCRIPTION
from pyanaconda.modules.common.task import sync_run_task
Expand Down Expand Up @@ -80,6 +81,16 @@ def _prepare_configuration(payload, ksdata):
configuration_queue.queue_started.connect(lambda x: progress_message(x.status_message))
configuration_queue.task_completed.connect(lambda x: progress_step(x.name))

# import certificates first
# they may be required for subscription, initramfs regenerating, ... ?
if is_module_available(SECURITY):
certificates_import = TaskQueue("Certificates import", N_("Importing certificates"))
certificates_proxy = SECURITY.get_proxy(CERTIFICATES)
certificates_import.append_dbus_tasks(SECURITY, [
certificates_proxy.InstallWithTask()
])
configuration_queue.append(certificates_import)

# add installation tasks for the Subscription DBus module
if is_module_available(SUBSCRIPTION):
# we only run the tasks if the Subscription module is available
Expand Down Expand Up @@ -305,6 +316,11 @@ def _prepare_installation(payload, ksdata):
fips_task = security_proxy.PreconfigureFIPSWithTask(payload.type)
pre_install.append_dbus_tasks(SECURITY, [fips_task])

# Import certificates so they are available for rpm scripts
certificates_proxy = SECURITY.get_proxy(CERTIFICATES)
certificates_task = certificates_proxy.PreInstallWithTask(payload.type)
pre_install.append_dbus_tasks(SECURITY, [certificates_task])

# Install the payload.
pre_install.append(Task("Find additional packages & run pre_install()", payload.pre_install))
installation_queue.append(pre_install)
Expand Down
2 changes: 2 additions & 0 deletions pyanaconda/kickstart.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@ def setupSections(self):
self.registerSection(NullSection(self.handler, sectionOpen="%traceback"))
self.registerSection(NullSection(self.handler, sectionOpen="%packages"))
self.registerSection(NullSection(self.handler, sectionOpen="%addon"))
self.registerSection(NullSection(self.handler, sectionOpen="%certificate"))
self.registerSection(NullSection(self.handler.anaconda, sectionOpen="%anaconda"))


Expand All @@ -451,6 +452,7 @@ def setupSections(self):
self.registerSection(OnErrorScriptSection(self.handler, dataObj=self.scriptClass))
self.registerSection(UselessSection(self.handler, sectionOpen="%packages"))
self.registerSection(UselessSection(self.handler, sectionOpen="%addon"))
self.registerSection(UselessSection(self.handler, sectionOpen="%certificate"))
self.registerSection(AnacondaSection(self.handler.anaconda))


Expand Down
2 changes: 1 addition & 1 deletion pyanaconda/modules/boss/kickstart_manager/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
TrackedKickstartElements

VALID_SECTIONS_ANACONDA = ["%pre", "%pre-install", "%post", "%onerror", "%traceback",
"%packages", "%addon", "%anaconda"]
"%packages", "%addon", "%anaconda", "%certificate"]


class StoreSection(Section):
Expand Down
8 changes: 7 additions & 1 deletion pyanaconda/modules/common/constants/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from dasbus.identifier import DBusObjectIdentifier
from pyanaconda.modules.common.constants.namespaces import STORAGE_NAMESPACE, NETWORK_NAMESPACE, \
PARTITIONING_NAMESPACE, DEVICE_TREE_NAMESPACE, \
RHSM_NAMESPACE, BOSS_NAMESPACE
RHSM_NAMESPACE, BOSS_NAMESPACE, SECURITY_NAMESPACE

# Boss objects.

Expand Down Expand Up @@ -154,3 +154,9 @@
namespace=RHSM_NAMESPACE,
basename="Syspurpose"
)

# Security objects
CERTIFICATES = DBusObjectIdentifier(
namespace=SECURITY_NAMESPACE,
basename="Certificates"
)
58 changes: 58 additions & 0 deletions pyanaconda/modules/common/structures/security.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#
# DBus structures for the storage data.
#
# Copyright (C) 2025 Red Hat, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from dasbus.structure import DBusData
from dasbus.typing import * # pylint: disable=wildcard-import

__all__ = ["CertificateData"]


class CertificateData(DBusData):
"""Structure for the certificate data."""

def __init__(self):
self._filename = ""
self._cert = ""
self._dir = ""

@property
def filename(self) -> Str:
"""The certificate file name."""
return self._filename

@filename.setter
def filename(self, value: Str) -> None:
self._filename = value

@property
def cert(self) -> Str:
"""The certificate content."""
return self._cert

@cert.setter
def cert(self, value: Str) -> None:
self._cert = value

@property
def dir(self) -> Str:
"""The certificate directory."""
return self._dir

@dir.setter
def dir(self, value: Str) -> None:
self._dir = value
2 changes: 2 additions & 0 deletions pyanaconda/modules/security/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

SUBDIRS = certificates

pkgpyexecdir = $(pyexecdir)/py$(PACKAGE_NAME)
securitydir = $(pkgpyexecdir)/modules/security
security_PYTHON = $(srcdir)/*.py
Expand Down
21 changes: 21 additions & 0 deletions pyanaconda/modules/security/certificates/Makefile.am
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#
# Copyright (C) 2025 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

pkgpyexecdir = $(pyexecdir)/py$(PACKAGE_NAME)
certificatesdir = $(pkgpyexecdir)/modules/security/certificates
dist_certificates_DATA = $(wildcard $(srcdir)/*.py)

MAINTAINERCLEANFILES = Makefile.in
20 changes: 20 additions & 0 deletions pyanaconda/modules/security/certificates/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#
# Copyright (C) 2025 Red Hat, Inc.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions of
# the GNU General Public License v.2, or (at your option) any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY expressed or implied, including the implied warranties of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details. You should have received a copy of the
# GNU General Public License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
# source code or documentation are not subject to the GNU General Public
# License and may only be used or replicated with the express permission of
# Red Hat, Inc.
#
from pyanaconda.modules.security.certificates.certificates import CertificatesModule

__all__ = ["CertificatesModule"]
Loading

0 comments on commit 3c6eac9

Please sign in to comment.