diff --git a/edge_manager/edge_mgr_build_images.adoc b/edge_manager/edge_mgr_build_images.adoc new file mode 100644 index 0000000000..000bbd5b53 --- /dev/null +++ b/edge_manager/edge_mgr_build_images.adoc @@ -0,0 +1,343 @@ +[#edge-mgmt-build] += Building bootc image and disk image with the {rhem} + +Build a `bootc` operating system image that contains the {rhem} agent and build an operating system disk image for flashing to physical devices or virtual machines. + +For more information, read the following sections: + +* <> +* <> +* <> +* <> +* <> +* <> + +[#build-prerequisites] +== Prerequisites + +* Install the latest `flightctl` CLI. See <>. +* Install `podman` version 5.0 or higher. See (https://podman.io/docs/installation[Podman Installation Instructions]). +* Install `skopeo` version 1.14 or higher. +//Can't link to the GH repo for skopeo here. + +[#install-rhem-cli] +=== Installing the {rhem} CLI + +Complete the following steps: + +. Create a variable by selecting the appropriate {rhem} CLI binary for your operating system and CPU architecture: + ++ +[source,bash] +---- +EM_CLI_BINARY=flightctl-linux-amd64 +---- + +. Download the `flightctl` binary to your machine by running the following command: + ++ +[source,bash] +---- +curl -LO https://github.com/flightctl/flightctl/releases/latest/download/${EM_CLI_BINARY} +---- + +. Verify that the downloaded binary has the correct checksum by running the following command: + ++ +[source,bash] +---- +echo "$(curl -L -s https://github.com/flightctl/flightctl/releases/download/latest/${EM_CLI_BINARY}-sha256.txt) ${EM_CLI_BINARY}" | sha256sum --check +---- + +. If the checksum is correct, rename the file to `flightctl` and make the executable by running the following command: + ++ +[source,bash] +---- +$ mv "${EM_CLI_BINARY}" flightctl && chmod +x flightctl +---- + +[#request-cert] +== Optional: Requesting an enrollment certificate for early binding + +If you want to include an agent configuration in the image, then complete the following steps: + +. Authenticate with the {rhem} service by using the `flightctl` CLI. + ++ +[source,bash] +---- +flightctl login https://api.flightctl.127.0.0.1.nip.io/ --web --insecure-skip-tls-verify +---- + +. Obtain the enrollment credentials in the format of an agent configuration file by running the following command: + ++ +[source,bash] +---- +flightctl certificate request --signer=enrollment --expiration=365d --output=embedded > config.yaml <1> +---- +<1> The credentials are valid for a year. + ++ +The returned `config.yaml` contains the URLs of the {rhem} service, the certificate authority bundle, and the enrollment client certificate and key for the agent. +See the following example: + ++ +[source,yaml] +---- +enrollment-service: + authentication: + client-certificate-data: LS0tLS1CRUdJTiBD... + client-key-data: LS0tLS1CRUdJTiBF... + service: + certificate-authority-data: LS0tLS1CRUdJTiBD... + server: https://agent-api.flightctl.127.0.0.1.nip.io:7443 + enrollment-ui-endpoint: https://ui.flightctl.127.0.0.1.nip.io:8081 +---- + +[#build-bootc-image] +== Building the operating system image with `bootc` + +Build the operating system image with `bootc` that contains the {rhem} agent and the agent configuration. +Optionally, you can include any drivers, host configuration, and application workloads that you need. +Complete the following steps: + +. Create a file named `Containerfile` with the following content to build an operating system image based on RHEL 9 that includes the {rhem} agent and configuration: + ++ +[source,bash] +---- +FROM registry.redhat.io/rhel9/rhel-bootc: + +RUN dnf -y copr enable @redhat-et/flightctl rhel-9-x86_64 && \ + dnf -y install flightctl-agent && \ + dnf -y clean all && \ + systemctl enable flightctl-agent.service + +# Optional: to enable podman-compose application support uncomment below” +# RUN dnf -y install epel-release epel-next-release && \ +# dnf -y install podman-compose && \ +# systemctl enable podman.service + +ADD config.yaml /etc/flightctl/ +---- + +*Note:* The base image referenced in `FROM` is a bootable container (`bootc`) image that already contains a Linux kernel, which allows you to reuse existing standard container build tools and workflows. + +. Disable the default automatic updates by adding the following command to the `Containerfile`: + ++ +[source,bash] +---- +FROM registry.redhat.io/rhel9/rhel-bootc: + +RUN dnf -y copr enable @redhat-et/flightctl rhel-9-x86_64 && \ + dnf -y install flightctl-agent && \ + dnf -y clean all && \ + systemctl enable flightctl-agent.service && \ + systemctl mask bootc-fetch-apply-updates.timer +---- + +. Define the OCI registry by running the following command: + ++ +[source,bash] +---- +OCI_REGISTRY=quay.io +---- + +. Define the image repository that you have permissions to write to by running the following command: + ++ +[source,bash] +---- +OCI_IMAGE_REPO=${OCI_REGISTRY}// +---- + +. Define the image tag by running the following command: + ++ +[source,bash] +---- +OCI_IMAGE_TAG=v1 +---- + +. Build the operating system image for your target platform: + ++ +[source,bash] +---- +sudo podman build -t ${OCI_IMAGE_REPO}:${OCI_IMAGE_TAG} . +---- + +[#build-sign-image] +== Signing and publishing the `bootc` operating system image + +Sign the `bootc` operating system image by using Sigstore. +Complete the following steps: + +. Generate a Sigstore key pair named `signingkey.pub` and `signingkey.private`: + ++ +[source,bash] +---- +skopeo generate-sigstore-key --output-prefix signingkey +---- + +. Configure container tools like Podman and Skopeo to upload Sigstore signatures together with your signed image to your OCI registry: + ++ +[source,bash] +---- +sudo tee "/etc/containers/registries.d/${OCI_REGISTRY}.yaml" > /dev/null <> +* <> +* <> +* xref:../edge_manager/edge_mgmt_build_images.adoc#edge-mgmt-build[Building bootc image and disk image with the {rhem}] +//Link to updating and managing operating system? + +To start building images, see xref:../edge_management/edge_mgmt_build_images.adoc#edge-mgmt-build[Building bootc operating system image and disk image with {rhem}]. + +[#images-process] +== The image building process + +At a high level, the image building process works as follows: + +. Choose a base `bootc` operating system image, such as a Fedora, CentOS, or RHEL image. +. Create a container file that layers the following items onto the base `bootc` image: +* The {rhem} agent and configuration. +* Optional: Any drivers specific to your target deployment environment. +* Optional: Host configuration, for example certificate authority bundles, and application workloads that are common to all deployments. +. Build, publish, and sign a `bootc` operating system image using `podman` and `skopeo`. +. Create an operating system disk image by using `bootc-image-builder`. +. Build, publish, and sign an operating system disk image using `skopeo`. + +*Note:* The operating system disk image creates partitions, volume, and the file system on the device, which only needs to be done once, during provisioning. +For subsequent device updates, only the `bootc` operating system image is required, which contains the files in the file system. + +[#enrollment] +== Enrollment + +You need to enroll devices to a {rhem} service before you can start managing them. +The {rhem} agent that runs on a device handles the device enrollment. + +When the agent starts on a device, the agent searches for the configuration in the `/etc/flightctl/config.yaml` file. +The files defines the following configurations: + +* The enrollment endpoint, which is the {rhem} service that the agent connects to for enrollment. +* The enrollment certificate, which is the X.509 client certificate and key that the agent only uses to securely request enrollment from the {rhem} service. +* Optional: Any additional agent configuration. + +The agent starts the enrollment process by searching for the enrollment endpoint, the {rhem} service, that is defined in the configuration file. +After establishing a secure, mTLS-protected network connection with the service, the agent submits an enrollment request to the service. +The request includes a description of hardware and operating system of the device, a X.509 certificate signing request, and the cryptographic identity of the device. +The enrollment request must be approved by an authorized user. +After the request is approved, the device becomes trusted and managed by the {rhem} service. + +[#enrollment-methods] +=== Enrollment methods + +You can provision the enrollment endpoint and certificate to the device in the following ways: + +Early binding:: You can build an operating system image that includes the enrollment endpoint and certificate. +Devices using an early binding image can automatically connect to the defined {rhem} service to request enrollment, without depending on any provisioning infrastructure. +The devices share the same long-lived X.509 client certificate. +However, in this case, the devices are bound to a specific service and owner. + +Late binding:: You can define the enrollment endpoint and certificate at provisioning time instead of including them in the operating system image. +Devices using a late binding image are not bound to a single owner or service and can have device-specific, short-lived X.509 client certificates. +However, late binding requires virtualization or bare metal provisioning infrastructure that can request device-specific enrollment endpoints and certificates from the {rhem} service and inject them into the provisioned system by using mechanisms such as https://cloud-init.io/[cloud-init], https://coreos.github.io/ignition/supported-platforms/[Ignition], or https://anaconda-installer.readthedocs.io/en/latest/kickstart.html[kickstart]. + +Other:: You can build the operating system image that includes the agent configuration and the enrollment endpoint. +However, you define the enrollment certificate at provisioning. + +*Note:* The enrollment certificate is only used to secure the network connection for submitting an enrollment request. +The enrollment certificate is not involved in the actual verification or approval of the enrollment request. +The enrollment certificate is no longer used with enrolled devices, as the devices rely on device-specific management certificates instead. + +[#images-best-practices] +== Best practices for building images + +Consider the following best practices when you build images: + +* <> +* <> +* <> +* <> + +For more information, see https://containers.github.io/bootc/building/guidance.html[Generic guidance for building images] in the `bootc` documentation. + +[#buildtime-runtime] +=== Build-time configuration over dynamic runtime configuration + +Prefer adding configuration to the operating system image at build time. +Adding configuration at build time ensures that the configurations are tested, distributed, and updated together. +In cases when build-time configuration is not feasible or desirable, you can dynamically configure devices at runtime instead with the {rhem}. + +Dynamic runtime configuration is preferable in the following cases: +* You have a configuration that is deployment or site-specific, such as a hostname or a site-specific network credential. +* You have secrets that are not secure to distribute with the image. +* You have application workloads that need to be added, updated, or deleted without reboot or they are on a faster cadence than the operating system. + +//TODO For more information about configuring devices at runtime, see [Managing OS Configuration]. + +[#usr-etc] +=== Configuration in /usr over /etc + +Prefer placing configuration files in the `/usr` directory if the configuration is static and the application or service supports that configuration. +By placing the configuration in the `/usr` directory, the configuration remains read-only and fully defined by the image. + +//(avoiding ostree’s 3-way merge of `/etc` as a potential source of drift) + +It is not feasible or desirable to place the configuration in the `/usr` directory in the following cases: + +* The configuration is deployment or site-specific. +* The application or service only supports reading configuration from the `/etc` directory. +* The configuration might need to be changed at runtime. + +//(although most applications allow configuration in `/etc` to override that in `/usr`) + +[#drop-dir] +=== Drop-in directories + +To avoid deviation from the target configuration, avoid directly editing configuration files. +Instead, use drop-in directories to replace or remove configuration files that the service aggregates. + +*Note:* You can identify drop-in directories by the `.d/` at the end of the directory name. +For example, `/etc/containers/certs.d`, `/etc/cron.d`, and `/etc/NetworkManager/conf.d`. + +[#avoid-drift] +=== Operating system images with scripts + +Avoid executing scripts or commands that change the file system. +The `bootc` or the {rhem} can overwrite the changed files that might cause a deviation or failed integrity checks. + +Instead, run such scripts or commands during image building, so changes are part of the image. +Alternatively, use the configuration management mechanisms of the {rhem}. \ No newline at end of file diff --git a/edge_manager/main.adoc b/edge_manager/main.adoc index 59c90302cc..134d8d5d01 100644 --- a/edge_manager/main.adoc +++ b/edge_manager/main.adoc @@ -3,4 +3,6 @@ include::modules/common-attributes.adoc[] = Edge management include::edge_mgr_intro.adoc[leveloffset=+1] -include::edge_mgr_enable.adoc[leveloffset=+2] \ No newline at end of file +include::edge_mgr_enable.adoc[leveloffset=+2] +include::edge_mgr_images.adoc[leveloffset=+2] +include::edge_mgr_build_images.adoc[leveloffset=+3]