From b8937303cef45a9552be9eb3518038bb06c41128 Mon Sep 17 00:00:00 2001 From: Jared Baur Date: Tue, 13 Jun 2023 11:38:28 -0700 Subject: [PATCH] nixos/tee-supplicant: add tee-supplicant module The tee-supplicant is a program that interacts with OP-TEE OS and allows loading trusted applications at runtime (among other things). There is an `optee` test included that uses the pkcs11 trusted application (in upstream OP-TEE OS), loads it during system startup via tee-supplicant, and uses `pkcs11-tool` to list available token slots. --- .../manual/release-notes/rl-2505.section.md | 2 + nixos/modules/module-list.nix | 1 + .../services/misc/tee-supplicant/default.nix | 95 +++++++++++++++++++ nixos/tests/all-tests.nix | 1 + nixos/tests/optee.nix | 72 ++++++++++++++ 5 files changed, 171 insertions(+) create mode 100644 nixos/modules/services/misc/tee-supplicant/default.nix create mode 100644 nixos/tests/optee.nix diff --git a/nixos/doc/manual/release-notes/rl-2505.section.md b/nixos/doc/manual/release-notes/rl-2505.section.md index 44e8e6b84364..694c8caadb2c 100644 --- a/nixos/doc/manual/release-notes/rl-2505.section.md +++ b/nixos/doc/manual/release-notes/rl-2505.section.md @@ -222,6 +222,8 @@ Alongside many enhancements to NixOS modules and general system improvements, th - [Limine](https://github.com/limine-bootloader/limine) a modern, advanced, portable, multiprotocol bootloader and boot manager. Available as [boot.loader.limine](#opt-boot.loader.limine.enable). +- [tee-supplicant](https://github.com/OP-TEE/optee_client), a userspace supplicant for OP-TEE OS. Available as [services.tee-supplicant](#opt-services.tee-supplicant.enable). + - [Orthanc](https://orthanc.uclouvain.be/) a lightweight, RESTful DICOM server for healthcare and medical research. Available as [services.orthanc](#opt-services.orthanc.enable). - [Docling Serve](https://github.com/docling-project/docling-serve) running [Docling](https://github.com/docling-project/docling) as an API service. Available as [services.docling-serve](#opt-services.docling-serve.enable). diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index a297dfc96d5d..6d4c9672f822 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -930,6 +930,7 @@ ./services/misc/taskchampion-sync-server.nix ./services/misc/taskserver ./services/misc/tautulli.nix + ./services/misc/tee-supplicant ./services/misc/tiddlywiki.nix ./services/misc/tp-auto-kbbl.nix ./services/misc/transfer-sh.nix diff --git a/nixos/modules/services/misc/tee-supplicant/default.nix b/nixos/modules/services/misc/tee-supplicant/default.nix new file mode 100644 index 000000000000..185253e2c44d --- /dev/null +++ b/nixos/modules/services/misc/tee-supplicant/default.nix @@ -0,0 +1,95 @@ +{ + config, + pkgs, + lib, + ... +}: +let + inherit (lib) + getExe' + mkEnableOption + mkIf + mkOption + mkPackageOption + types + ; + + cfg = config.services.tee-supplicant; + + taDir = "optee_armtz"; + + trustedApplications = pkgs.linkFarm "runtime-trusted-applications" ( + map ( + ta: + let + # This is safe since we are using it as the path value, so the context + # will still ensure that this nix store path exists on the running + # system. + taFile = builtins.baseNameOf (builtins.unsafeDiscardStringContext ta); + in + { + name = "lib/${taDir}/${taFile}"; + path = ta; + } + ) cfg.trustedApplications + ); +in +{ + options.services.tee-supplicant = { + enable = mkEnableOption "OP-TEE userspace supplicant"; + + package = mkPackageOption pkgs "optee-client" { }; + + trustedApplications = mkOption { + type = types.listOf types.path; + default = [ ]; + description = '' + A list of full paths to trusted applications that will be loaded at + runtime by tee-supplicant. + ''; + }; + + pluginPath = mkOption { + type = types.path; + default = "/run/current-system/sw/lib/tee-supplicant/plugins"; + description = '' + The directory where plugins will be loaded from on startup. + ''; + }; + + reeFsParentPath = mkOption { + type = types.path; + default = "/var/lib/tee"; + description = '' + The directory where the secure filesystem will be stored in the rich + execution environment (REE FS). + ''; + }; + }; + + config = mkIf cfg.enable { + environment = mkIf (cfg.trustedApplications != [ ]) { + systemPackages = [ trustedApplications ]; + pathsToLink = [ "/lib/${taDir}" ]; + }; + + systemd.services.tee-supplicant = { + description = "Userspace supplicant for OPTEE-OS"; + + serviceConfig = { + ExecStart = toString [ + (getExe' cfg.package "tee-supplicant") + "--ta-dir ${taDir}" + "--fs-parent-path ${cfg.reeFsParentPath}" + "--plugin-path ${cfg.pluginPath}" + ]; + Restart = "always"; + }; + + after = [ "modprobe@optee.service" ]; + wants = [ "modprobe@optee.service" ]; + + wantedBy = [ "multi-user.target" ]; + }; + }; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 11d6afa5478e..441dbec25a0c 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -1064,6 +1064,7 @@ in openvscode-server = runTest ./openvscode-server.nix; open-webui = runTest ./open-webui.nix; openvswitch = runTest ./openvswitch.nix; + optee = handleTestOn [ "aarch64-linux" ] ./optee.nix { }; orangefs = runTest ./orangefs.nix; os-prober = handleTestOn [ "x86_64-linux" ] ./os-prober.nix { }; osquery = handleTestOn [ "x86_64-linux" ] ./osquery.nix { }; diff --git a/nixos/tests/optee.nix b/nixos/tests/optee.nix new file mode 100644 index 000000000000..ac049b30378c --- /dev/null +++ b/nixos/tests/optee.nix @@ -0,0 +1,72 @@ +import ./make-test-python.nix ( + { pkgs, lib, ... }: + { + name = "optee"; + + meta = with pkgs.lib.maintainers; { + maintainers = [ jmbaur ]; + }; + + nodes.machine = + { config, pkgs, ... }: + let + inherit (pkgs) armTrustedFirmwareQemu opteeQemuAarch64 ubootQemuAarch64; + + # Default environment for qemu-arm64 uboot does not work well with + # large nixos kernel/initrds. + uboot = ubootQemuAarch64.overrideAttrs (old: { + postPatch = + (old.postPatch or "") + + '' + substituteInPlace board/emulation/qemu-arm/qemu-arm.env \ + --replace-fail "ramdisk_addr_r=0x44000000" "ramdisk_addr_r=0x46000000" + ''; + }); + + bios = armTrustedFirmwareQemu.override { + extraMakeFlags = [ + "SPD=opteed" + "BL32=${opteeQemuAarch64}/tee-header_v2.bin" + "BL32_EXTRA1=${opteeQemuAarch64}/tee-pager_v2.bin" + "BL32_EXTRA2=${opteeQemuAarch64}/tee-pageable_v2.bin" + "BL33=${uboot}/u-boot.bin" + "all" + "fip" + ]; + filesToInstall = [ + "build/qemu/release/bl1.bin" + "build/qemu/release/fip.bin" + ]; + postInstall = '' + dd if=$out/bl1.bin of=$out/bios.bin bs=4096 conv=notrunc + dd if=$out/fip.bin of=$out/bios.bin seek=64 bs=4096 conv=notrunc + ''; + }; + in + { + virtualisation = { + inherit bios; + cores = 2; + qemu.options = [ + "-machine virt,secure=on,accel=tcg,gic-version=2" + "-cpu cortex-a57" + ]; + }; + + # VM boots up via qfw + boot.loader.grub.enable = false; + + services.tee-supplicant = { + enable = true; + # pkcs11 trusted application + trustedApplications = [ "${opteeQemuAarch64.devkit}/ta/fd02c9da-306c-48c7-a49c-bbd827ae86ee.ta" ]; + }; + }; + testScript = '' + machine.wait_for_unit("tee-supplicant.service") + out = machine.succeed("${pkgs.opensc}/bin/pkcs11-tool --module ${lib.getLib pkgs.optee-client}/lib/libckteec.so --list-token-slots") + if out.find("OP-TEE PKCS11 TA") < 0: + raise Exception("optee pkcs11 token not found") + ''; + } +)