summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorJörg Thalheim <Mic92@users.noreply.github.com>2021-12-13 14:16:26 +0000
committerGitHub <noreply@github.com>2021-12-13 14:16:26 +0000
commitafa3c99cd5ea99dbf3f599506570be5d69b2aba1 (patch)
treedd566bcf19d25241274a313d1b4d0bbffdf1702c /nixos
parent0dadd5fabb2a10d39e2641b5b7857ea1131517a6 (diff)
parentd6cc0ad96e0a8723d457d0a8c6c6bfc54df2920b (diff)
downloadnixpkgs-afa3c99cd5ea99dbf3f599506570be5d69b2aba1.tar
nixpkgs-afa3c99cd5ea99dbf3f599506570be5d69b2aba1.tar.gz
nixpkgs-afa3c99cd5ea99dbf3f599506570be5d69b2aba1.tar.bz2
nixpkgs-afa3c99cd5ea99dbf3f599506570be5d69b2aba1.tar.lz
nixpkgs-afa3c99cd5ea99dbf3f599506570be5d69b2aba1.tar.xz
nixpkgs-afa3c99cd5ea99dbf3f599506570be5d69b2aba1.tar.zst
nixpkgs-afa3c99cd5ea99dbf3f599506570be5d69b2aba1.zip
Merge pull request #148593 from veehaitch/sgx-psw
sgx-psw: init package and module
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-2205.section.xml12
-rw-r--r--nixos/doc/manual/release-notes/rl-2205.section.md2
-rw-r--r--nixos/modules/hardware/cpu/intel-sgx.nix47
-rw-r--r--nixos/modules/module-list.nix2
-rw-r--r--nixos/modules/services/security/aesmd.nix227
-rw-r--r--nixos/tests/aesmd.nix62
-rw-r--r--nixos/tests/all-tests.nix1
7 files changed, 351 insertions, 2 deletions
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
index c84a3e3b019..144b277438a 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
@@ -19,8 +19,16 @@
   </section>
   <section xml:id="sec-release-22.05-new-services">
     <title>New Services</title>
-    <para>
-    </para>
+    <itemizedlist spacing="compact">
+      <listitem>
+        <para>
+          <link xlink:href="https://github.com/intel/linux-sgx#install-the-intelr-sgx-psw">aesmd</link>,
+          the Intel SGX Architectural Enclave Service Manager. Available
+          as
+          <link linkend="opt-services.aesmd.enable">services.aesmd</link>.
+        </para>
+      </listitem>
+    </itemizedlist>
   </section>
   <section xml:id="sec-release-22.05-incompatibilities">
     <title>Backward Incompatibilities</title>
diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md
index 45ed69cf1b0..4418c8142a1 100644
--- a/nixos/doc/manual/release-notes/rl-2205.section.md
+++ b/nixos/doc/manual/release-notes/rl-2205.section.md
@@ -8,6 +8,8 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 ## New Services {#sec-release-22.05-new-services}
 
+- [aesmd](https://github.com/intel/linux-sgx#install-the-intelr-sgx-psw), the Intel SGX Architectural Enclave Service Manager. Available as [services.aesmd](#opt-services.aesmd.enable).
+
 ## Backward Incompatibilities {#sec-release-22.05-incompatibilities}
 
 - `pkgs.ghc` now refers to `pkgs.targetPackages.haskellPackages.ghc`.
diff --git a/nixos/modules/hardware/cpu/intel-sgx.nix b/nixos/modules/hardware/cpu/intel-sgx.nix
new file mode 100644
index 00000000000..04647940058
--- /dev/null
+++ b/nixos/modules/hardware/cpu/intel-sgx.nix
@@ -0,0 +1,47 @@
+{ config, lib, ... }:
+with lib;
+let
+  cfg = config.hardware.cpu.intel.sgx.provision;
+  defaultGroup = "sgx_prv";
+in
+{
+  options.hardware.cpu.intel.sgx.provision = {
+    enable = mkEnableOption "access to the Intel SGX provisioning device";
+    user = mkOption {
+      description = "Owner to assign to the SGX provisioning device.";
+      type = types.str;
+      default = "root";
+    };
+    group = mkOption {
+      description = "Group to assign to the SGX provisioning device.";
+      type = types.str;
+      default = defaultGroup;
+    };
+    mode = mkOption {
+      description = "Mode to set for the SGX provisioning device.";
+      type = types.str;
+      default = "0660";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = hasAttr cfg.user config.users.users;
+        message = "Given user does not exist";
+      }
+      {
+        assertion = (cfg.group == defaultGroup) || (hasAttr cfg.group config.users.groups);
+        message = "Given group does not exist";
+      }
+    ];
+
+    users.groups = optionalAttrs (cfg.group == defaultGroup) {
+      "${cfg.group}" = { };
+    };
+
+    services.udev.extraRules = ''
+      SUBSYSTEM=="misc", KERNEL=="sgx_provision", OWNER="${cfg.user}", GROUP="${cfg.group}", MODE="${cfg.mode}"
+    '';
+  };
+}
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 3cc9ea88e17..1f826220a0f 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -45,6 +45,7 @@
   ./hardware/ckb-next.nix
   ./hardware/cpu/amd-microcode.nix
   ./hardware/cpu/intel-microcode.nix
+  ./hardware/cpu/intel-sgx.nix
   ./hardware/corectrl.nix
   ./hardware/digitalbitbox.nix
   ./hardware/device-tree.nix
@@ -928,6 +929,7 @@
   ./services/search/kibana.nix
   ./services/search/meilisearch.nix
   ./services/search/solr.nix
+  ./services/security/aesmd.nix
   ./services/security/certmgr.nix
   ./services/security/cfssl.nix
   ./services/security/clamav.nix
diff --git a/nixos/modules/services/security/aesmd.nix b/nixos/modules/services/security/aesmd.nix
new file mode 100644
index 00000000000..bb53bc49e25
--- /dev/null
+++ b/nixos/modules/services/security/aesmd.nix
@@ -0,0 +1,227 @@
+{ config, pkgs, lib, ... }:
+with lib;
+let
+  cfg = config.services.aesmd;
+
+  sgx-psw = pkgs.sgx-psw.override { inherit (cfg) debug; };
+
+  configFile = with cfg.settings; pkgs.writeText "aesmd.conf" (
+    concatStringsSep "\n" (
+      optional (whitelistUrl != null) "whitelist url = ${whitelistUrl}" ++
+      optional (proxy != null) "aesm proxy = ${proxy}" ++
+      optional (proxyType != null) "proxy type = ${proxyType}" ++
+      optional (defaultQuotingType != null) "default quoting type = ${defaultQuotingType}" ++
+      # Newline at end of file
+      [ "" ]
+    )
+  );
+in
+{
+  options.services.aesmd = {
+    enable = mkEnableOption "Intel's Architectural Enclave Service Manager (AESM) for Intel SGX";
+    debug = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Whether to build the PSW package in debug mode.";
+    };
+    settings = mkOption {
+      description = "AESM configuration";
+      default = { };
+      type = types.submodule {
+        options.whitelistUrl = mkOption {
+          type = with types; nullOr str;
+          default = null;
+          example = "http://whitelist.trustedservices.intel.com/SGX/LCWL/Linux/sgx_white_list_cert.bin";
+          description = "URL to retrieve authorized Intel SGX enclave signers.";
+        };
+        options.proxy = mkOption {
+          type = with types; nullOr str;
+          default = null;
+          example = "http://proxy_url:1234";
+          description = "HTTP network proxy.";
+        };
+        options.proxyType = mkOption {
+          type = with types; nullOr (enum [ "default" "direct" "manual" ]);
+          default = if (cfg.settings.proxy != null) then "manual" else null;
+          example = "default";
+          description = ''
+            Type of proxy to use. The <literal>default</literal> uses the system's default proxy.
+            If <literal>direct</literal> is given, uses no proxy.
+            A value of <literal>manual</literal> uses the proxy from
+            <option>services.aesmd.settings.proxy</option>.
+          '';
+        };
+        options.defaultQuotingType = mkOption {
+          type = with types; nullOr (enum [ "ecdsa_256" "epid_linkable" "epid_unlinkable" ]);
+          default = null;
+          example = "ecdsa_256";
+          description = "Attestation quote type.";
+        };
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [{
+      assertion = !(config.boot.specialFileSystems."/dev".options ? "noexec");
+      message = "SGX requires exec permission for /dev";
+    }];
+
+    hardware.cpu.intel.sgx.provision.enable = true;
+
+    systemd.services.aesmd =
+      let
+        storeAesmFolder = "${sgx-psw}/aesm";
+        # Hardcoded path AESM_DATA_FOLDER in psw/ae/aesm_service/source/oal/linux/aesm_util.cpp
+        aesmDataFolder = "/var/opt/aesmd/data";
+        aesmStateDirSystemd = "%S/aesmd";
+      in
+      {
+        description = "Intel Architectural Enclave Service Manager";
+        wantedBy = [ "multi-user.target" ];
+
+        after = [
+          "auditd.service"
+          "network.target"
+          "syslog.target"
+        ];
+
+        environment = {
+          NAME = "aesm_service";
+          AESM_PATH = storeAesmFolder;
+          LD_LIBRARY_PATH = storeAesmFolder;
+        };
+
+        # Make sure any of the SGX application enclave devices is available
+        unitConfig.AssertPathExists = [
+          # legacy out-of-tree driver
+          "|/dev/isgx"
+          # DCAP driver
+          "|/dev/sgx/enclave"
+          # in-tree driver
+          "|/dev/sgx_enclave"
+        ];
+
+        serviceConfig = rec {
+          ExecStartPre = pkgs.writeShellScript "copy-aesmd-data-files.sh" ''
+            set -euo pipefail
+            whiteListFile="${aesmDataFolder}/white_list_cert_to_be_verify.bin"
+            if [[ ! -f "$whiteListFile" ]]; then
+              ${pkgs.coreutils}/bin/install -m 644 -D \
+                "${storeAesmFolder}/data/white_list_cert_to_be_verify.bin" \
+                "$whiteListFile"
+            fi
+          '';
+          ExecStart = "${sgx-psw}/bin/aesm_service --no-daemon";
+          ExecReload = ''${pkgs.coreutils}/bin/kill -SIGHUP "$MAINPID"'';
+
+          Restart = "on-failure";
+          RestartSec = "15s";
+
+          DynamicUser = true;
+          Group = "sgx";
+          SupplementaryGroups = [
+            config.hardware.cpu.intel.sgx.provision.group
+          ];
+
+          Type = "simple";
+
+          WorkingDirectory = storeAesmFolder;
+          StateDirectory = "aesmd";
+          StateDirectoryMode = "0700";
+          RuntimeDirectory = "aesmd";
+          RuntimeDirectoryMode = "0750";
+
+          # Hardening
+
+          # chroot into the runtime directory
+          RootDirectory = "%t/aesmd";
+          BindReadOnlyPaths = [
+            builtins.storeDir
+            # Hardcoded path AESM_CONFIG_FILE in psw/ae/aesm_service/source/utils/aesm_config.cpp
+            "${configFile}:/etc/aesmd.conf"
+          ];
+          BindPaths = [
+            # Hardcoded path CONFIG_SOCKET_PATH in psw/ae/aesm_service/source/core/ipc/SocketConfig.h
+            "%t/aesmd:/var/run/aesmd"
+            "%S/aesmd:/var/opt/aesmd"
+          ];
+
+          # PrivateDevices=true will mount /dev noexec which breaks AESM
+          PrivateDevices = false;
+          DevicePolicy = "closed";
+          DeviceAllow = [
+            # legacy out-of-tree driver
+            "/dev/isgx rw"
+            # DCAP driver
+            "/dev/sgx rw"
+            # in-tree driver
+            "/dev/sgx_enclave rw"
+            "/dev/sgx_provision rw"
+          ];
+
+          # Requires Internet access for attestation
+          PrivateNetwork = false;
+
+          RestrictAddressFamilies = [
+            # Allocates the socket /var/run/aesmd/aesm.socket
+            "AF_UNIX"
+            # Uses the HTTP protocol to initialize some services
+            "AF_INET"
+            "AF_INET6"
+          ];
+
+          # True breaks stuff
+          MemoryDenyWriteExecute = false;
+
+          # needs the ipc syscall in order to run
+          SystemCallFilter = [
+            "@system-service"
+            "~@aio"
+            "~@chown"
+            "~@clock"
+            "~@cpu-emulation"
+            "~@debug"
+            "~@keyring"
+            "~@memlock"
+            "~@module"
+            "~@mount"
+            "~@privileged"
+            "~@raw-io"
+            "~@reboot"
+            "~@resources"
+            "~@setuid"
+            "~@swap"
+            "~@sync"
+            "~@timer"
+          ];
+          SystemCallArchitectures = "native";
+          SystemCallErrorNumber = "EPERM";
+
+          CapabilityBoundingSet = "";
+          KeyringMode = "private";
+          LockPersonality = true;
+          NoNewPrivileges = true;
+          NotifyAccess = "none";
+          PrivateMounts = true;
+          PrivateTmp = true;
+          PrivateUsers = true;
+          ProcSubset = "pid";
+          ProtectClock = true;
+          ProtectControlGroups = true;
+          ProtectHome = true;
+          ProtectHostname = true;
+          ProtectKernelLogs = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          ProtectProc = "invisible";
+          ProtectSystem = "strict";
+          RemoveIPC = true;
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          RestrictSUIDSGID = true;
+          UMask = "0066";
+        };
+      };
+  };
+}
diff --git a/nixos/tests/aesmd.nix b/nixos/tests/aesmd.nix
new file mode 100644
index 00000000000..59c04fe7e96
--- /dev/null
+++ b/nixos/tests/aesmd.nix
@@ -0,0 +1,62 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }: {
+  name = "aesmd";
+  meta = {
+    maintainers = with lib.maintainers; [ veehaitch ];
+  };
+
+  machine = { lib, ... }: {
+    services.aesmd = {
+      enable = true;
+      settings = {
+        defaultQuotingType = "ecdsa_256";
+        proxyType = "direct";
+        whitelistUrl = "http://nixos.org";
+      };
+    };
+
+    # Should have access to the AESM socket
+    users.users."sgxtest" = {
+      isNormalUser = true;
+      extraGroups = [ "sgx" ];
+    };
+
+    # Should NOT have access to the AESM socket
+    users.users."nosgxtest".isNormalUser = true;
+
+    # We don't have a real SGX machine in NixOS tests
+    systemd.services.aesmd.unitConfig.AssertPathExists = lib.mkForce [ ];
+  };
+
+  testScript = ''
+    with subtest("aesmd.service starts"):
+      machine.wait_for_unit("aesmd.service")
+      status, main_pid = machine.systemctl("show --property MainPID --value aesmd.service")
+      assert status == 0, "Could not get MainPID of aesmd.service"
+      main_pid = main_pid.strip()
+
+    with subtest("aesmd.service runtime directory permissions"):
+      runtime_dir = "/run/aesmd";
+      res = machine.succeed(f"stat -c '%a %U %G' {runtime_dir}").strip()
+      assert "750 aesmd sgx" == res, f"{runtime_dir} does not have the expected permissions: {res}"
+
+    with subtest("aesm.socket available on host"):
+      socket_path = "/var/run/aesmd/aesm.socket"
+      machine.wait_until_succeeds(f"test -S {socket_path}")
+      machine.succeed(f"test 777 -eq $(stat -c '%a' {socket_path})")
+      for op in [ "-r", "-w", "-x" ]:
+        machine.succeed(f"sudo -u sgxtest test {op} {socket_path}")
+        machine.fail(f"sudo -u nosgxtest test {op} {socket_path}")
+
+    with subtest("Copies white_list_cert_to_be_verify.bin"):
+      whitelist_path = "/var/opt/aesmd/data/white_list_cert_to_be_verify.bin"
+      whitelist_perms = machine.succeed(
+        f"nsenter -m -t {main_pid} ${pkgs.coreutils}/bin/stat -c '%a' {whitelist_path}"
+      ).strip()
+      assert "644" == whitelist_perms, f"white_list_cert_to_be_verify.bin has permissions {whitelist_perms}"
+
+    with subtest("Writes and binds aesm.conf in service namespace"):
+      aesmd_config = machine.succeed(f"nsenter -m -t {main_pid} ${pkgs.coreutils}/bin/cat /etc/aesmd.conf")
+
+      assert aesmd_config == "whitelist url = http://nixos.org\nproxy type = direct\ndefault quoting type = ecdsa_256\n", "aesmd.conf differs"
+  '';
+})
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 06305460c6a..f86cc2544da 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -23,6 +23,7 @@ in
 {
   _3proxy = handleTest ./3proxy.nix {};
   acme = handleTest ./acme.nix {};
+  aesmd = handleTest ./aesmd.nix {};
   agda = handleTest ./agda.nix {};
   airsonic = handleTest ./airsonic.nix {};
   amazon-init-shell = handleTest ./amazon-init-shell.nix {};