Files
Philip Kannegaard Hayes 29c30249c3 aesmd: fix failure on first run and clean up old DCAP compat
On our prod machines, aesmd would always fail the first time after boot,
with logs like:
`XXX-copy-aesmd-data-files.sh[770]: chown: invalid user: 'aesmd:aesmd'`.
Everything would be fine after the next restart, but blocking boot for
15 seconds is definitely annoying.

It appears the issue was that we were trying to name the DynamicUser
User and Group (aesmd/aesmd) during ExecStartPre, but systemd only
creates these right before ExecStart. After playing around with the
ExecStartPre script, I finally got it to work correctly. As a bonus, we
can also re-add the RootDirectory/chroot hardening.

I've also cleaned out the config for the old out-of-tree isgx driver and
the old DCAP driver. The kernel has had in-kernel SGX support since 5.11
(~2021/02).
2026-03-24 10:41:12 -07:00

272 lines
8.0 KiB
Nix

{
config,
options,
pkgs,
lib,
...
}:
let
inherit (lib)
concatStringsSep
literalExpression
makeLibraryPath
mkEnableOption
mkIf
mkOption
mkPackageOption
mkRemovedOptionModule
optional
types
;
cfg = config.services.aesmd;
opt = options.services.aesmd;
sgx-psw = cfg.package;
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
{
imports = [
(mkRemovedOptionModule [ "services" "aesmd" "debug" ] ''
Enable debug mode by overriding the aesmd package directly:
services.aesmd.package = pkgs.sgx-psw.override { debug = true; };
'')
];
options.services.aesmd = {
enable = mkEnableOption "Intel's Architectural Enclave Service Manager (AESM) for Intel SGX";
package = mkPackageOption pkgs "sgx-psw" { };
environment = mkOption {
type = with types; attrsOf str;
default = { };
description = "Additional environment variables to pass to the AESM service.";
# Example environment variable for `sgx-azure-dcap-client` provider library
example = {
AZDCAP_COLLATERAL_VERSION = "v2";
AZDCAP_DEBUG_LOG_LEVEL = "INFO";
};
};
quoteProviderLibrary = mkOption {
type = with types; nullOr path;
default = null;
example = literalExpression "pkgs.sgx-azure-dcap-client";
description = "Custom quote provider library to use.";
};
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;
defaultText = literalExpression ''
if (config.${opt.settings}.proxy != null) then "manual" else null
'';
example = "default";
description = ''
Type of proxy to use. The `default` uses the system's default proxy.
If `direct` is given, uses no proxy.
A value of `manual` uses the proxy from
{option}`services.aesmd.settings.proxy`.
'';
};
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";
in
{
description = "Intel Architectural Enclave Service Manager";
wantedBy = [ "multi-user.target" ];
after = [
"auditd.service"
"network.target"
];
environment = {
NAME = "aesm_service";
AESM_PATH = storeAesmFolder;
LD_LIBRARY_PATH = makeLibraryPath [ cfg.quoteProviderLibrary ];
}
// cfg.environment;
# Ensure the SGX application enclave device is available
unitConfig.AssertPathExists = [ "/dev/sgx_enclave" ];
serviceConfig = {
# Run with elevated privileges to create /var/opt/aesmd/... before
# dropping to DynamicUser.
ExecStartPre = ''
+${lib.getExe' pkgs.coreutils "install"} -m 644 -D \
"${storeAesmFolder}/data/white_list_cert_to_be_verify.bin" \
"${aesmDataFolder}/white_list_cert_to_be_verify.bin"
'';
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 ---
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 = [
# 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"
# Makes HTTPS requests to the Intel PCCS service (or a cache).
"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";
};
};
};
}