diff --git a/build-tests/x86/tumbleweed/test-image-nitro-enclave/appliance.kiwi b/build-tests/x86/tumbleweed/test-image-nitro-enclave/appliance.kiwi
new file mode 100644
index 0000000000..73640eb4aa
--- /dev/null
+++ b/build-tests/x86/tumbleweed/test-image-nitro-enclave/appliance.kiwi
@@ -0,0 +1,60 @@
+
+
+
+
+ Marcus Schäfer
+ marcus.schaefer@suse.com
+ AWS Nitro Enclave test build
+
+
+
+
+
+
+ 1.1.1
+ zypper
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build-tests/x86/tumbleweed/test-image-nitro-enclave/config.sh b/build-tests/x86/tumbleweed/test-image-nitro-enclave/config.sh
new file mode 100644
index 0000000000..60fda26719
--- /dev/null
+++ b/build-tests/x86/tumbleweed/test-image-nitro-enclave/config.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+set -ex
+
+declare kiwi_iname=${kiwi_iname}
+
+echo "Configure image: [${kiwi_iname}]..."
+
+#======================================
+# Setup services
+#--------------------------------------
+for service in nitro-enclave-alive sshd;do
+ systemctl enable "${service}"
+done
+
+#======================================
+# Allow ssh root login
+#--------------------------------------
+echo "PermitRootLogin yes" > /etc/ssh/sshd_config.d/root.conf
+
+#======================================
+# load virtio_mmio
+#--------------------------------------
+echo virtio_mmio > /etc/modules-load.d/virtio-mmio.conf
diff --git a/doc/source/building_images.rst b/doc/source/building_images.rst
index d8451e208e..22324dbc9b 100644
--- a/doc/source/building_images.rst
+++ b/doc/source/building_images.rst
@@ -17,3 +17,4 @@ Building Images for Supported Types
building_images/build_container_image
building_images/build_wsl_container
building_images/build_kis
+ building_images/build_enclave
diff --git a/doc/source/building_images/build_enclave.rst b/doc/source/building_images/build_enclave.rst
new file mode 100644
index 0000000000..03eabec9d8
--- /dev/null
+++ b/doc/source/building_images/build_enclave.rst
@@ -0,0 +1,103 @@
+.. _eif:
+
+Build an AWS Nitro Enclave
+==============================
+
+.. sidebar:: Abstract
+
+ This page explains how to build AWS Nitro Enclaves. It covers the following topics:
+
+ * how to build an AWS Nitro Enclave
+ * how to test the enclave via QEMU
+
+AWS Nitro Enclaves enables customers to create isolated compute environments
+to further protect and securely process highly sensitive data such as personally
+identifiable information (PII), healthcare, financial, and intellectual property
+data within their Amazon EC2 instances. Nitro Enclaves uses the same Nitro
+Hypervisor technology that provides CPU and memory isolation for EC2 instances.
+For further details please visit https://aws.amazon.com/ec2/nitro/nitro-enclaves
+
+To add an enclave build to your appliance, create a `type` element with
+`image` set to `enclave` in the :file:`config.xml` file as shown below:
+
+.. code:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+The following attributes of the `type` element are relevant:
+
+- `enclave_format`: Specifies the enclave target
+
+ As of today only the `aws-nitro` enclave target is supported
+
+
+- `kernelcmdline`: Specifies the kernel commandline suitable for the enclave
+
+ An enclave is a system that runs completely in RAM loaded from
+ an enclave binary format which includes the kernel, initrd and
+ the kernel commandline suitable for the target system.
+
+With the appropriate settings specified in :file:`config.xml`, you can build an
+image using {kiwi}:
+
+.. code:: bash
+
+ $ sudo kiwi-ng system build \
+ --description kiwi/build-tests/{exc_description_enclave} \
+ --set-repo {exc_repo_tumbleweed} \
+ --target-dir /tmp/myimage
+
+The resulting image is saved in :file:`/tmp/myimage`, and the image can
+be tested with QEMU:
+
+.. code:: bash
+
+ $ sudo qemu-system-x86_64 \
+ -M nitro-enclave,vsock=c \
+ -m 4G \
+ -nographic \
+ -chardev socket,id=c,path=/tmp/vhost4.socket \
+ -kernel {exc_image_base_name_enclave}.eif
+
+The image is now complete and ready to use. Access to the system is
+possible via ssh through a vsock connection into the guest. To establish
+a vsock connection it's required to forward the connection through the
+guest AF_VSOCK socket. This can be done via a ProxyCommand setup of the
+host ssh as follows:
+
+.. code:: bash
+
+ $ vi ~/bin/vsock-ssh.sh
+
+ #!/bin/bash
+ CID=$(echo "$1" | cut -d . -f 1)
+ socat - VSOCK-CONNECT:$CID:22
+
+.. code:: bash
+
+ $ vi ~/.ssh/config
+
+ host *.vsock
+ ProxyCommand ~/bin/vsock-ssh.sh %h
+
+After the ssh proxy setup login to the enclave with a custom vsock port
+as follows:
+
+.. code:: bash
+
+ $ ssh root@21.vsock
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 50ab4e5dfc..65ed220ee4 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -70,6 +70,7 @@ def setup(app):
'{exc_image_base_name_disk_simple}': 'kiwi-test-image-disk-simple',
'{exc_image_base_name_live}': 'kiwi-test-image-live',
'{exc_image_base_name_docker}': 'kiwi-test-image-docker',
+ '{exc_image_base_name_enclave}': 'kiwi-test-image-nitro-enclave',
'{exc_netboot}': 'netboot/suse-tumbleweed',
'{exc_description_pxe}': 'x86/tumbleweed/test-image-pxe',
'{exc_description_vagrant}': 'x86/leap/test-image-vagrant',
@@ -78,6 +79,7 @@ def setup(app):
'{exc_description_live}': 'x86/leap/test-image-live',
'{exc_description_wsl}': 'x86/tumbleweed/test-image-wsl',
'{exc_description_docker}': 'x86/leap/test-image-docker',
+ '{exc_description_enclave}': 'x86/tumbleweed/test-image-nitro-enclave',
'{exc_os_version}': '15.5',
'{exc_image_version}': '1.15.3',
'{exc_repo_leap}': 'obs://openSUSE:Leap:15.5/standard',
diff --git a/doc/source/image_types_and_results.rst b/doc/source/image_types_and_results.rst
index 510948c0e6..e19f39549e 100644
--- a/doc/source/image_types_and_results.rst
+++ b/doc/source/image_types_and_results.rst
@@ -48,6 +48,11 @@ KIS Root File System Image
Many different deployment strategies are possible.
For further details refer to: :ref:`kis`
+AWS Nitro Enclave
+ An initrd based image using the `eif` binary format. The image is
+ expected to be used in the AWS Nitro Enclave system or for testing
+ in QEMU. For further details refer to: :ref:`eif`
+
Image Results
-------------
diff --git a/kiwi/builder/__init__.py b/kiwi/builder/__init__.py
index 53128a09fa..4e6d732bb3 100644
--- a/kiwi/builder/__init__.py
+++ b/kiwi/builder/__init__.py
@@ -49,6 +49,8 @@ def new(
name_token = ('live', 'LiveImageBuilder')
elif image_type in Defaults.get_kis_image_types():
name_token = ('kis', 'KisBuilder')
+ elif image_type in Defaults.get_enclaves_image_types():
+ name_token = ('enclave', 'EnclaveBuilder')
elif image_type in Defaults.get_archive_image_types():
name_token = ('archive', 'ArchiveBuilder')
elif image_type in Defaults.get_container_image_types():
diff --git a/kiwi/builder/enclave.py b/kiwi/builder/enclave.py
new file mode 100644
index 0000000000..3203bcbf14
--- /dev/null
+++ b/kiwi/builder/enclave.py
@@ -0,0 +1,195 @@
+# Copyright (c) 2024 SUSE Software Solutions Germany GmbH. All rights reserved.
+#
+# This file is part of kiwi.
+#
+# kiwi 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 3 of the License, or
+# (at your option) any later version.
+#
+# kiwi 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 kiwi. If not, see
+#
+import os
+import logging
+from typing import Dict
+
+# project
+from kiwi.defaults import Defaults
+from kiwi.boot.image import BootImage
+from kiwi.system.setup import SystemSetup
+from kiwi.system.kernel import Kernel
+from kiwi.system.result import Result
+from kiwi.runtime_config import RuntimeConfig
+from kiwi.xml_state import XMLState
+from kiwi.command import Command
+
+from kiwi.exceptions import (
+ KiwiEnclaveFormatError,
+ KiwiEnclaveBootImageError
+)
+
+log = logging.getLogger('kiwi')
+
+
+class EnclaveBuilder:
+ """
+ **Enclave Builder**
+
+ Enclaves defines initrd-only image types.
+
+ :param object xml_state: instance of :class:`XMLState`
+ :param str target_dir: target directory path name
+ :param str root_dir: system image root directory
+ :param dict custom_args: Custom processing arguments defined as hash keys:
+ * signing_keys: list of package signing keys
+ * xz_options: string of XZ compression parameters
+ """
+ def __init__(
+ self, xml_state: XMLState, target_dir: str,
+ root_dir: str, custom_args: Dict = None
+ ):
+ self.target_dir = target_dir
+ self.custom_cmdline = xml_state.build_type.get_kernelcmdline()
+ self.format = xml_state.build_type.get_enclave_format()
+
+ self.system_setup = SystemSetup(
+ xml_state=xml_state, root_dir=root_dir
+ )
+ xml_state.build_type.set_initrd_system('kiwi')
+ xml_state.build_type.set_boot(f'{root_dir}/image')
+
+ self.boot_signing_keys = custom_args['signing_keys'] if custom_args \
+ and 'signing_keys' in custom_args else None
+
+ self.xz_options = custom_args['xz_options'] if custom_args \
+ and 'xz_options' in custom_args else None
+
+ self.boot_image_task = BootImage.new(
+ xml_state, target_dir, root_dir,
+ signing_keys=self.boot_signing_keys
+ )
+ # Force BootImageKiwi instance to use existing root_dir
+ self.boot_image_task.boot_root_directory = root_dir
+
+ self.bundle_format = xml_state.get_build_type_bundle_format()
+ self.image_name = ''.join(
+ [
+ target_dir, '/',
+ xml_state.xml_data.get_name(),
+ '.' + Defaults.get_platform_name(),
+ '-' + xml_state.get_image_version()
+ ]
+ )
+ self.image: str = ''
+ self.initrd: str = ''
+ self.kernel_filename: str = ''
+ self.enclave: str = ''
+ self.result = Result(xml_state)
+ self.runtime_config = RuntimeConfig()
+
+ def create(self) -> Result:
+ """
+ Build an eif image using the eif-cli
+
+ Image types which triggers this builder are:
+
+ * image="enclave"
+
+ :return: result
+
+ :rtype: instance of :class:`Result`
+ """
+ if not self.format:
+ raise KiwiEnclaveFormatError(
+ 'No enclave_format= specified in build type'
+ )
+
+ # Create initrd
+ self.boot_image_task.create_initrd()
+
+ # extract kernel from boot system
+ kernel = Kernel(self.boot_image_task.boot_root_directory)
+ kernel_data = kernel.get_kernel()
+ if kernel_data:
+ self.kernel_filename = ''.join(
+ [
+ os.path.basename(self.image_name), '-',
+ kernel_data.version, '.kernel'
+ ]
+ )
+ kernel.copy_kernel(
+ self.target_dir, self.kernel_filename
+ )
+ else:
+ raise KiwiEnclaveBootImageError(
+ 'No kernel in boot image tree %s found' %
+ self.boot_image_task.boot_root_directory
+ )
+
+ self.initrd = os.path.basename(self.boot_image_task.initrd_filename)
+
+ if self.format == 'aws-nitro':
+ self.enclave = self.image_name + ".eif"
+ Command.run(
+ [
+ 'eif_build',
+ '--kernel', '/'.join([self.target_dir, self.kernel_filename]),
+ '--ramdisk', '/'.join([self.target_dir, self.initrd]),
+ '--cmdline', self.custom_cmdline,
+ '--output', self.enclave
+ ]
+ )
+
+ Result.verify_image_size(
+ self.runtime_config.get_max_size_constraint(),
+ self.initrd
+ )
+ # store image bundle_format in result
+ if self.bundle_format:
+ self.result.add_bundle_format(self.bundle_format)
+
+ self.result.add(
+ key='enclave',
+ filename=self.enclave,
+ use_for_bundle=True,
+ compress=self.runtime_config.get_bundle_compression(
+ default=False
+ ),
+ shasum=True
+ )
+
+ # create image root metadata
+ self.result.add(
+ key='image_packages',
+ filename=self.system_setup.export_package_list(
+ self.target_dir
+ ),
+ use_for_bundle=True,
+ compress=False,
+ shasum=False
+ )
+ self.result.add(
+ key='image_changes',
+ filename=self.system_setup.export_package_changes(
+ self.target_dir
+ ),
+ use_for_bundle=True,
+ compress=True,
+ shasum=False
+ )
+ self.result.add(
+ key='image_verified',
+ filename=self.system_setup.export_package_verification(
+ self.target_dir
+ ),
+ use_for_bundle=True,
+ compress=False,
+ shasum=False
+ )
+ return self.result
diff --git a/kiwi/defaults.py b/kiwi/defaults.py
index abe67d411d..0deb1707c5 100644
--- a/kiwi/defaults.py
+++ b/kiwi/defaults.py
@@ -1646,6 +1646,17 @@ def get_kis_image_types():
"""
return ['kis', 'pxe']
+ @staticmethod
+ def get_enclaves_image_types():
+ """
+ Provides supported enclave(initrd-only) image types
+
+ :return: enclave image type names
+
+ :rtype: list
+ """
+ return ['enclave']
+
@staticmethod
def get_boot_image_description_path():
"""
diff --git a/kiwi/exceptions.py b/kiwi/exceptions.py
index 8c2d1e31bd..63f8e85fdf 100644
--- a/kiwi/exceptions.py
+++ b/kiwi/exceptions.py
@@ -492,6 +492,13 @@ class KiwiKisBootImageError(KiwiError):
"""
+class KiwiEnclaveBootImageError(KiwiError):
+ """
+ Exception raised if no kernel image was found while
+ building an enclave image.
+ """
+
+
class KiwiRaidSetupError(KiwiError):
"""
Exception raised if invalid or not enough user data is provided
@@ -853,3 +860,10 @@ class KiwiBootLoaderDiskPasswordError(KiwiError):
"""
Exception raised if the disk password could not be set
"""
+
+
+class KiwiEnclaveFormatError(KiwiError):
+ """
+ Exception raised if no enclave_format attribute specified
+ for the selected build type
+ """
diff --git a/kiwi/schema/kiwi.rnc b/kiwi/schema/kiwi.rnc
index a6ddae0285..a6e099a229 100644
--- a/kiwi/schema/kiwi.rnc
+++ b/kiwi/schema/kiwi.rnc
@@ -1869,6 +1869,13 @@ div {
sch:param [ name = "attr" value = "format" ]
sch:param [ name = "types" value = "oem" ]
]
+ k.type.enclave_format.attribute =
+ ## Specifies the format of the virtual disk.
+ attribute enclave_format { "aws-nitro" }
+ >> sch:pattern [ id = "enclave_format" is-a = "image_type"
+ sch:param [ name = "attr" value = "enclave_format" ]
+ sch:param [ name = "types" value = "enclave" ]
+ ]
k.type.formatoptions.attribute =
## Specifies additional format options passed on to qemu-img
## formatoptions is a comma separated list of format specific
@@ -1942,7 +1949,7 @@ div {
attribute image {
"btrfs" | "cpio" | "docker" | "ext2" | "ext3" |
"ext4" | "iso" | "oem" | "pxe" | "kis" | "squashfs" | "tbz" |
- "xfs" | "oci" | "appx"
+ "xfs" | "oci" | "appx" | "enclave"
}
>> sch:pattern [
id = "metadata_path_mandatory" is-a = "image_type_requirement"
@@ -2023,7 +2030,7 @@ div {
attribute kernelcmdline { text }
>> sch:pattern [ id = "kernelcmdline" is-a = "image_type"
sch:param [ name = "attr" value = "kernelcmdline" ]
- sch:param [ name = "types" value = "oem iso pxe kis" ]
+ sch:param [ name = "types" value = "oem iso pxe kis enclave" ]
]
k.type.luks_version.attribute =
## Specify LUKS version. This can be either set to "luks", "luks1"
@@ -2261,6 +2268,7 @@ div {
k.type.editbootinstall.attribute? &
k.type.filesystem.attribute? &
k.type.flags.attribute? &
+ k.type.enclave_format.attribute? &
k.type.format.attribute? &
k.type.formatoptions.attribute? &
k.type.fsmountoptions.attribute? &
diff --git a/kiwi/schema/kiwi.rng b/kiwi/schema/kiwi.rng
index b50bf6f790..e4d4b66ac9 100644
--- a/kiwi/schema/kiwi.rng
+++ b/kiwi/schema/kiwi.rng
@@ -2690,6 +2690,16 @@ a different set of live features.
+
+
+ Specifies the format of the virtual disk.
+ aws-nitro
+
+
+
+
+
+
Specifies additional format options passed on to qemu-img
@@ -2801,6 +2811,7 @@ initrd architecture.
xfs
oci
appx
+ enclave
@@ -2908,7 +2919,7 @@ kernel command line options
-
+
@@ -3266,6 +3277,9 @@ kiwi-ng result bundle ...
+
+
+
diff --git a/kiwi/xml_parse.py b/kiwi/xml_parse.py
index d6168ed838..620367a5be 100644
--- a/kiwi/xml_parse.py
+++ b/kiwi/xml_parse.py
@@ -3082,7 +3082,7 @@ class type_(GeneratedsSuper):
"""The Image Type of the Logical Extend"""
subclass = None
superclass = None
- def __init__(self, boot=None, bootfilesystem=None, firmware=None, bootkernel=None, bootpartition=None, bootpartsize=None, efipartsize=None, efifatimagesize=None, eficsm=None, efiparttable=None, dosparttable_extended_layout=None, bootprofile=None, btrfs_quota_groups=None, btrfs_root_is_snapshot=None, btrfs_root_is_subvolume=None, btrfs_set_default_volume=None, btrfs_root_is_readonly_snapshot=None, compressed=None, devicepersistency=None, editbootconfig=None, editbootinstall=None, filesystem=None, flags=None, format=None, formatoptions=None, fsmountoptions=None, fscreateoptions=None, squashfscompression=None, gcelicense=None, hybridpersistent=None, hybridpersistent_filesystem=None, gpt_hybrid_mbr=None, force_mbr=None, initrd_system=None, image=None, metadata_path=None, installboot=None, install_continue_on_timeout=None, installprovidefailsafe=None, installiso=None, installstick=None, installpxe=None, mediacheck=None, kernelcmdline=None, luks=None, luks_version=None, luksOS=None, luks_randomize=None, luks_pbkdf=None, mdraid=None, overlayroot=None, overlayroot_write_partition=None, overlayroot_readonly_partsize=None, verity_blocks=None, embed_verity_metadata=None, standalone_integrity=None, embed_integrity_metadata=None, integrity_legacy_hmac=None, integrity_metadata_key_description=None, integrity_keyfile=None, primary=None, ramonly=None, rootfs_label=None, spare_part=None, spare_part_mountpoint=None, spare_part_fs=None, spare_part_fs_attributes=None, spare_part_is_last=None, target_blocksize=None, target_removable=None, selinux_policy=None, vga=None, vhdfixedtag=None, volid=None, application_id=None, wwid_wait_timeout=None, derived_from=None, delta_root=None, ensure_empty_tmpdirs=None, xen_server=None, publisher=None, disk_start_sector=None, root_clone=None, boot_clone=None, bundle_format=None, bootloader=None, containerconfig=None, machine=None, oemconfig=None, size=None, systemdisk=None, partitions=None, vagrantconfig=None, installmedia=None, luksformat=None):
+ def __init__(self, boot=None, bootfilesystem=None, firmware=None, bootkernel=None, bootpartition=None, bootpartsize=None, efipartsize=None, efifatimagesize=None, eficsm=None, efiparttable=None, dosparttable_extended_layout=None, bootprofile=None, btrfs_quota_groups=None, btrfs_root_is_snapshot=None, btrfs_root_is_subvolume=None, btrfs_set_default_volume=None, btrfs_root_is_readonly_snapshot=None, compressed=None, devicepersistency=None, editbootconfig=None, editbootinstall=None, filesystem=None, flags=None, enclave_format=None, format=None, formatoptions=None, fsmountoptions=None, fscreateoptions=None, squashfscompression=None, gcelicense=None, hybridpersistent=None, hybridpersistent_filesystem=None, gpt_hybrid_mbr=None, force_mbr=None, initrd_system=None, image=None, metadata_path=None, installboot=None, install_continue_on_timeout=None, installprovidefailsafe=None, installiso=None, installstick=None, installpxe=None, mediacheck=None, kernelcmdline=None, luks=None, luks_version=None, luksOS=None, luks_randomize=None, luks_pbkdf=None, mdraid=None, overlayroot=None, overlayroot_write_partition=None, overlayroot_readonly_partsize=None, verity_blocks=None, embed_verity_metadata=None, standalone_integrity=None, embed_integrity_metadata=None, integrity_legacy_hmac=None, integrity_metadata_key_description=None, integrity_keyfile=None, primary=None, ramonly=None, rootfs_label=None, spare_part=None, spare_part_mountpoint=None, spare_part_fs=None, spare_part_fs_attributes=None, spare_part_is_last=None, target_blocksize=None, target_removable=None, selinux_policy=None, vga=None, vhdfixedtag=None, volid=None, application_id=None, wwid_wait_timeout=None, derived_from=None, delta_root=None, ensure_empty_tmpdirs=None, xen_server=None, publisher=None, disk_start_sector=None, root_clone=None, boot_clone=None, bundle_format=None, bootloader=None, containerconfig=None, machine=None, oemconfig=None, size=None, systemdisk=None, partitions=None, vagrantconfig=None, installmedia=None, luksformat=None):
self.original_tagname_ = None
self.boot = _cast(None, boot)
self.bootfilesystem = _cast(None, bootfilesystem)
@@ -3107,6 +3107,7 @@ def __init__(self, boot=None, bootfilesystem=None, firmware=None, bootkernel=Non
self.editbootinstall = _cast(None, editbootinstall)
self.filesystem = _cast(None, filesystem)
self.flags = _cast(None, flags)
+ self.enclave_format = _cast(None, enclave_format)
self.format = _cast(None, format)
self.formatoptions = _cast(None, formatoptions)
self.fsmountoptions = _cast(None, fsmountoptions)
@@ -3316,6 +3317,8 @@ def get_filesystem(self): return self.filesystem
def set_filesystem(self, filesystem): self.filesystem = filesystem
def get_flags(self): return self.flags
def set_flags(self, flags): self.flags = flags
+ def get_enclave_format(self): return self.enclave_format
+ def set_enclave_format(self, enclave_format): self.enclave_format = enclave_format
def get_format(self): return self.format
def set_format(self, format): self.format = format
def get_formatoptions(self): return self.formatoptions
@@ -3596,6 +3599,9 @@ def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='
if self.flags is not None and 'flags' not in already_processed:
already_processed.add('flags')
outfile.write(' flags=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.flags), input_name='flags')), ))
+ if self.enclave_format is not None and 'enclave_format' not in already_processed:
+ already_processed.add('enclave_format')
+ outfile.write(' enclave_format=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.enclave_format), input_name='enclave_format')), ))
if self.format is not None and 'format' not in already_processed:
already_processed.add('format')
outfile.write(' format=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.format), input_name='format')), ))
@@ -3973,6 +3979,11 @@ def buildAttributes(self, node, attrs, already_processed):
already_processed.add('flags')
self.flags = value
self.flags = ' '.join(self.flags.split())
+ value = find_attr_value_('enclave_format', node)
+ if value is not None and 'enclave_format' not in already_processed:
+ already_processed.add('enclave_format')
+ self.enclave_format = value
+ self.enclave_format = ' '.join(self.enclave_format.split())
value = find_attr_value_('format', node)
if value is not None and 'format' not in already_processed:
already_processed.add('format')
diff --git a/test/unit/builder/enclave_test.py b/test/unit/builder/enclave_test.py
new file mode 100644
index 0000000000..bb50adeec6
--- /dev/null
+++ b/test/unit/builder/enclave_test.py
@@ -0,0 +1,108 @@
+from collections import namedtuple
+from unittest.mock import (
+ patch, Mock, MagicMock
+)
+from pytest import (
+ raises, fixture
+)
+import kiwi
+
+from kiwi.builder.enclave import EnclaveBuilder
+from kiwi.exceptions import (
+ KiwiEnclaveBootImageError,
+ KiwiEnclaveFormatError
+)
+
+
+class TestEnclaveBuilder:
+ @fixture(autouse=True)
+ def inject_fixtures(self, caplog):
+ self._caplog = caplog
+
+ @patch('kiwi.builder.enclave.BootImage')
+ def setup(self, mock_boot):
+ self.setup = Mock()
+ self.runtime_config = Mock()
+ self.runtime_config.get_max_size_constraint = Mock(
+ return_value=None
+ )
+ kiwi.builder.enclave.RuntimeConfig = Mock(
+ return_value=self.runtime_config
+ )
+ kiwi.builder.enclave.SystemSetup = Mock(
+ return_value=self.setup
+ )
+ self.boot_image_task = MagicMock()
+ self.boot_image_task.boot_root_directory = 'initrd_dir'
+ self.boot_image_task.initrd_filename = 'initrd_file_name'
+ mock_boot.new.return_value = self.boot_image_task
+ self.xml_state = Mock()
+ self.xml_state.profiles = None
+ self.xml_state.get_image_version = Mock(
+ return_value='1.2.3'
+ )
+ self.xml_state.get_initrd_system = Mock(
+ return_value='dracut'
+ )
+ self.xml_state.xml_data.get_name = Mock(
+ return_value='some-image'
+ )
+ self.xml_state.build_type = Mock()
+ self.xml_state.build_type.get_kernelcmdline = Mock(
+ return_value='some'
+ )
+ kernel_type = namedtuple(
+ 'kernel', ['filename', 'version']
+ )
+ self.kernel = Mock()
+ self.kernel.get_kernel = Mock(
+ return_value=kernel_type(filename='some-kernel', version='42')
+ )
+ kiwi.builder.enclave.Kernel = Mock(
+ return_value=self.kernel
+ )
+ self.enclave = EnclaveBuilder(
+ self.xml_state, 'target_dir', 'root_dir',
+ custom_args={'signing_keys': ['key_file_a', 'key_file_b']}
+ )
+ self.enclave.compressed = True
+
+ @patch('kiwi.builder.enclave.BootImage')
+ def setup_method(self, cls, mock_boot):
+ self.setup()
+
+ @patch('kiwi.builder.enclave.BootImage')
+ def test_create_invalid_enclave_format(self, mock_boot):
+ self.enclave.format = ''
+ with raises(KiwiEnclaveFormatError):
+ self.enclave.create()
+
+ @patch('kiwi.builder.enclave.Command.run')
+ def test_create_aws_nitro(self, mock_Command_run):
+ self.enclave.format = 'aws-nitro'
+ self.boot_image_task.required = Mock(
+ return_value=True
+ )
+ self.enclave.create()
+
+ self.boot_image_task.create_initrd.assert_called_once_with()
+ self.setup.export_package_list.assert_called_once_with(
+ 'target_dir'
+ )
+ self.setup.export_package_verification.assert_called_once_with(
+ 'target_dir'
+ )
+ mock_Command_run.assert_called_once_with(
+ [
+ 'eif_build',
+ '--kernel', 'target_dir/some-image.x86_64-1.2.3-42.kernel',
+ '--ramdisk', 'target_dir/initrd_file_name',
+ '--cmdline', 'some',
+ '--output', 'target_dir/some-image.x86_64-1.2.3.eif'
+ ]
+ )
+
+ def test_create_no_kernel_found(self):
+ self.kernel.get_kernel.return_value = False
+ with raises(KiwiEnclaveBootImageError):
+ self.enclave.create()
diff --git a/test/unit/builder/init_test.py b/test/unit/builder/init_test.py
index c70450d692..443dc39f81 100644
--- a/test/unit/builder/init_test.py
+++ b/test/unit/builder/init_test.py
@@ -61,6 +61,17 @@ def test_kis_builder(self, mock_builder):
xml_state, 'target_dir', 'root_dir', None
)
+ @patch('kiwi.builder.enclave.EnclaveBuilder')
+ def test_enclave_builder(self, mock_builder):
+ xml_state = Mock()
+ xml_state.get_build_type_name = Mock(
+ return_value='enclave'
+ )
+ ImageBuilder.new(xml_state, 'target_dir', 'root_dir')
+ mock_builder.assert_called_once_with(
+ xml_state, 'target_dir', 'root_dir', None
+ )
+
@patch('kiwi.builder.archive.ArchiveBuilder')
def test_archive_builder(self, mock_builder):
xml_state = Mock()
diff --git a/tox.ini b/tox.ini
index 369f8fbf99..fe791d19da 100644
--- a/tox.ini
+++ b/tox.ini
@@ -49,6 +49,7 @@ basepython =
check: python3
devel: python3
packagedoc: python3
+ doc: python3
passenv =
*
usedevelop = True