From 7bc5b4a7a582010209c3bd21f2308a59db28f7aa Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Fri, 26 May 2023 12:25:32 +0200 Subject: [PATCH] Add ISU guide Change-Id: Id7c3e0082883568f9a6d18db75a0c0aab6f8f7df --- doc/GUIDE.md | 427 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 427 insertions(+) create mode 100644 doc/GUIDE.md diff --git a/doc/GUIDE.md b/doc/GUIDE.md new file mode 100644 index 0000000..5aa313e --- /dev/null +++ b/doc/GUIDE.md @@ -0,0 +1,427 @@ +# Start + +The ISU (Individual Service Upgrade) is a mechanism that is used to upgrade a single +service on read-only storage without performing a full system upgrade. + + +# Quick start guide + +This section gives a brief introduction on how to add an ISU package to an existing +repository. + +To make this example practical, assume the `dlog_logger` service from the `dlog` +repository is going to be provided by the ISU package. + + +1. Add ISU configuration + + Create `packaging/isu.cfg` + + [isu] + name=#NAME# + version=#VERSION# + system_service= + + [files] + + + The `#NAME#` and `#VERSION#` patterns will be replaced by the RPM `%isu_package` + macro into the name and version of the RPM package. + + The `` must contain the exact `systemd` service file name used to + activate the service provided by the ISU package. + + The `` must contain a list of all files that should be added to the ISU + package (similarly to the `%files` section in the RPM `.spec`). + + + Our `dlog` example would look as follows: + + [isu] + name=#NAME# + version=#VERSION# + system_service=dlog_logger.service + + [files] + /usr/bin/dlog_logger + + +2. Prepare service file + + The service file is the only "entry point" to the ISU package, meaning it's the only + thing that will actually run from the ISU package. + + Typically, while enabling ISU functionality in an existing package, the original service + should be modified for ISU purposes. + + Every non-trivial appliaction should be run in a special sandbox, causing the ISU-provided + service to see its files in the place of standard system locations. ISU provides special + utility and helper variables to facilitate the proces of setting up the sandbox. Package + paths must be specified explicitly, though. + + `/etc/isu/service-common.inc` contains helper variables that can be used for setting up + the sandbox. + + Our example `packaging/isu/dlog_logger.service` file: + + [Unit] + Description=dlog logging service + Requires=local-fs.target + After=systemd-tmpfiles-setup.service local-fs.target + Before=basic.target + DefaultDependencies=no + + [Service] + Type=notify + Environment=TZ=:/etc/localtime + NotifyAccess=all + User=log + Group=log + SmackProcessLabel=System + + EnvironmentFile=/etc/isu/service-common.inc + ExecStart=/bin/isu-sandbox $ISU_SANDBOX_INVOCATION \ + --bind $ISU_RUN_DIR/dlog_logger/rootfs/usr/bin/dlog_logger /usr/bin/dlog_logger \ + --cap-add CAP_SYSLOG \ + /usr/bin/dlog_logger + AmbientCapabilities=CAP_SETFCAP CAP_SETUID CAP_SETGID + SecureBits=keep-caps + Restart=on-failure + LimitNOFILE=16384 + Nice=1 + + [Install] + WantedBy=basic.target + + Please note that compared to the original `dlog_logger.service` the following changes have been made for ISU: + + - `EnvironmentFile=` has been specified to include ISU-specific variables (used below) + + - `ExecStart=` has been changed to launch sandbox application with default parameters (`ISU_SANDBOX_INVOCATION`), + ISU-provided `$ISU_RUN_DIR/dlog_logger/rootfs/usr/bin/dlog_logger` is visible as `/usr/bin/dlog_logger` in the sandbox, + special capability has been added specifically for `dlog_logger` (`CAP_SYSLOG`) + + - `NotifyAccess=all` has been set as there is one intermediate process (`isu-sandbox`) beween `systemd` and `dlog_logger` + + - `AmbientCapabilities=` have been set to allow `isu-sandbox` to setup the sandbox environment properly + + Last but not least - the ISU `systemd` service file name MUST have exactly the same name as the original system + service (e.g., if the original service was named `dlog_logger.service`, then the ISU service file MUST also be + named `dlog_logger.service`). + +3. Install ISU files + + The ISU package is genenerated from a platform image, which must contain ISU configuration files in standard, + pre-configured locations. To create an ISU dlog package, we need to install our configuration files: + + Modify `packaging/dlog.spec` as follows: + + %isu_package + + %install + mkdir -p %{buildroot}/etc/isu/%{name}/system-services + install -m644 packaging/isu.cfg %{buildroot}/etc/isu/%{name}/isu.cfg + install -m644 packaging/isu/dlog_logger.service %{buildroot}/etc/isu/system-services/dlog_logger.service + + The above changes will cause the configuration to be placed in the correct location with all ISU-related files + packaged into the `%{name}-isu` package (thanks to the `%isu_package` macro). + + Please note that the new package is not installed automatically, and the final dlog ISU package must be + generated afterwards. + + To include ISU configuration you may add the following tag: + + Requires: %{name}-isu + + which will cause ISU package to be installed when the original package is installed. + + +4. Generate ISU package + + Generating an ISU package requires building a platform image first. Therefore, it won't be explained here, + as it's not something that an ordinary user would want or need to do. + + +# Documentation +## ISU package +The ISU package is the ZIP archive file that contains the following structure: + + isu.cfg + system-services/ + user-services/ + rootfs.img + checksum.sha256 + checksum.sha256.sign + +* `icu.cfg` - The INI format configuration file that contains the following data: + + [isu] + name = + version = + system_service = + + [files] + + + [info] + tz_build_release_name = + tz_build_arch = + tz_build_date = + model_name = + manufacturer = + device_type = /`. + +4. Run the service provided by the ISU Package instead of the one on the system. + + The `isu-generator`, which uses the `systemd-generator` mechanism, is + responsible for mounting the `rootfs.img` provided by the ISU package and launching + the upgraded service. For a description see **"Run the service provided by ISU package"**. + +## 1. ISU configuration +### isu.cfg + +The main directory where the files required by ISU package generator are located is: + + /etc/isu// + +The file structure in this directory is as follows: + +* `isu.cfg` - The main configuration file. +* `system-services/ `- Directory for system services. +* `user-services/` - Directory for user services. + +`isu.cfg` is an INI format config file which contains the following data: + + [isu] + name= + version= + system_service= + user_service + + [files] + + +* **name** - the name of the ISU Package (TODO). +* **version** - the version of the service file. Based on this, there will be a way + to decide whether there is a new or older version of the service on the + target system and whether to run the one provided by the system or by the ISU + Package. +* **service_file_name** - space separated names of the system service file in + the `system-services` subdirectory. If the package does not provide a system + or a user service then the corresponding entry should not be given. +* **file_list** - A list of file paths that are required for the operation of the + service and will be included in the package. + +In the `system-services` and/or `user-services` the appropriate service file should +be placed. + +### Service file + +The service file should run the service with the files provided by the ISU package. +The `rootfs.img` file from an ISU package is mounted in +`/run/isu//rootfs`, so the binary should be run, for example, from the +`/run/isu//rootfs/usr/bin/my_service`. + +If the service requires other files provided by ISU package to operate, it +should look for them in the above location. To change the path of loading +libraries it is possible to use for example the `LD_LIBRARY_PATH` system variable. + +If the service is not adapted to operate in another location (`/run/isu/...`), +the `isu-sandbox` can be used. + +### isu-sandbox + +`isu-sandbox` is a modified `Bubblewrap` which uses the `User namespaces` mechanism +to run applications in a sandboxed environment. +It can run the process in an environment that can be prepared with bind mounts +of the required directories. Thus, there is no need to modify the service so +that it can run in the changed location (`/run/isu/...`). + +The following example shows how to run `dlog_logger` in sandboxed environment: + + ExecStart=/bin/isu-sandbox \ + --proc /proc \ + --dev-bind /dev /dev \ + --ro-bind /etc /etc \ + --ro-bind /bin /bin \ + --ro-bind /lib /lib \ + --ro-bind-try /lib64 /lib64 \ + --bind /var /var \ + --bind /hal /hal \ + --bind /sys /sys \ + --bind /run /run \ + --bind /opt /opt \ + --bind /usr /usr \ + --bind /run/isu/dlog_logger/rootfs/usr/bin/dlog_logger /usr/bin/dlog_logger \ + --suid-bind \ + --map-all-uids \ + --cap-add CAP_SYSLOG \ + /usr/bin/dlog_logger + +`isu-sandbox` is preparing the directory structure of the new root for the `dlog_logger` process. +As you can see, the file `dlog_logger` provided by the ISU package in the new environment is +located in `/usr/bin/dlog_logger`. + +`isu-sandbox` requires a few more changes in the service file: +1. Ambient capabilities are required for `isu-sandbox` to be able to change the + UID/GID and capabilities of the target process. So add this line to the service file: + + AmbientCapabilities=CAP_SETFCAP CAP_SETUID CAP_SETGID + +2. In case the service type is `notify` the `NotifyAccess` option should be set + to `all`, because the parent of the spawned process is the isu-sandbox. +3. In case the service requires additional capabilities, they must be added to + the `isu-sandbox` command line using the `--cap-add` option. +4. If the service requires the libraries provided in the ISU package, + the `LD_LIBRARY_PATH` environment variable should be set accordingly. + +To simplify the configuration, you can use the prepared environment variables file: +`EnvironmentFile=/etc/isu/service-common.inc` +which contains a predefined `isu-sandbox` invocation with all standard bindings, +so the above example should be written as: + + EnvironmentFile=/etc/isu/service-common.inc + ExecStart=/bin/isu-sandbox $ISU_SANDBOX_INVOCATION \ + --bind $ISU_RUN_DIR/dlog_logger/rootfs/usr/bin/dlog_logger /usr/bin/dlog_logger \ + --cap-add CAP_SYSLOG \ + /usr/bin/dlog_logger + +A good usage example is the ISU configuration in the `platform/core/system/dlog` project. + +### Build RPM Package with ISU configuration + +The configuration for an ISU should be installed on the system by the RPM package +in the `/etc/isu//` path. +To simplify the preparation of the RPM package, you can use the `%isu_package` macro. +The macro will generate a new subpackage with the `isu` suffix that will contain all +the files installed in the `/etc/isu/` directory. +In addition, a substitution will be performed in all files found there: + +* `#NAME#` - name of the package (`%{name}`) +* `#VERSION#` - version of the package (`%{version}`) +* `#ISU_CONFIG_PATH#` - ISU configuration path (`/etc/isu`) +* `#ISU_INSTALL_PATH#` - ISU install path (`/opt/isu`) +* `#ISU_RUN_PATH#` - ISU run path (`/run/isu`) + +## 2. Make ISU Package + +ISU packages are generated based on the configuration found in `/etc/isu/` +subdirectories - one for each subdirectory. + +The process of preparing ISU package consists of the following steps: + +1. Search for all directories in `/etc/isu/` which contains `isu.cfg` file. +2. Mount images from the `tar.gz` archive. +3. Copy all files listed in the `[files]` section from the images to the + temporary directory. +4. Create a `SquashFS` image of the temporary directory. +5. Complete the `isu.cfg` configuration file with the data on the image + from which the package is generated. +6. Compute a checksum of the image, the configuration file and services files. +7. Sign the checksum +8. Create a ZIP archive + +The `isu_pkgs_maker.py` script has been prepared to automate this process. +Usage of the script: + + sudo ./isu_pkgs_maker.py --src --out --map --key + +* `src_dir` - Directory containing the generated image as a `.tar.gz` archive. +* `out_dir` - Directory where ISU packages will be saved as `.zip` archives. +* `map_file` - File which contains the image-to-path mapping. Example: + + [map] + rootfs.img=/ + system-data.img=/opt/ + +* `priv_key` - Private key with which the checksum will be signed. + Example of key generation: + + host$ openssl genrsa -out key.pem 2048 + host$ openssl rsa -in key.pem -pubout -out key.pub + +Since the script must mount the images, the root permissions are required. The +script will create ISU packages for all configuration files found in the +subdirectories of the `/etc/isu/` directory. + +## 3. Install ISU package + +To install the ISU Package, extract the contents of the ZIP archive to the +`/opt/isu//` directory. +The process of installation should include verification of file checksums and +the checksum file signature. + +## 4. Run the service provided by ISU package + +The `isu-generator` is responsible for launching the services provided by ISU +packages. This is a script that, using the `systemd-generator` mechanism, +dynamically generates service files that will be taken into account by `systemd` +at system startup. + +The script performs the following operations: +* scans `/opt/isu/*/` directories for `checksum.sha256.sign` and performs + signature check using hardcoded key path (must be changed by filling out the `$PUBKEY` variable) + +* verifies checksum as provided by the `/opt/isu/*/checksum.sha256` file + +* for every `.service` file found in `/opt/isu/*/{system-services,user-services}` the generator installs + and extends the original unit file to mount a `rootfs.img` from an ISU image + + - unit files are installed in the `/run/systemd/generator` directory (which overrides the `systemd` + service of the same name exisitng on the system) + + - note that for `user-services` the user session path is different and is defined when + the program is invoked by the `systemd --user` instance. + + - installs original unit name under `nonisu-SERVICE_NAME` for use in + case of ISU package failure + + - installs mount unit to mount ISU rootfs in `/run/isu/ISU_PKG_NAME/rootfs` + (eg. `run-isu-dlog-rootfs.mount`) + +Consequently, the following unit dependency tree is created: + + dlog_logger.service, requires: + └── run-isu-dlog-rootfs.mount + +On a running system, the ISU package can be seen under the `/run/isu/dlog` hierarchy as follows: + + / + └── run + └── isu + └── dlog + └── rootfs + ├── /usr/bin/dlog_logger + ├── /etc/dlog.conf.d + +As a fallback mechanism, the following procedure is used: +* original unit is copied to the `systemd-generator` directory as the `nonisu-NAME` + file, which can be referenced as a normal unit name +* new service file is automatically modified to include the following: + + OnFailure=nonisu-NAME + Conflicts=nonisu-NAME + + which will automatically start the original unit if the ISU package's + service fails to start or if the signature verification fails. -- 2.7.4